diff --git a/AGENTS.md b/AGENTS.md index 425ee28..4f87be1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,8 +4,10 @@ Swift-JSON requires Swift tools/compiler 6.4+, Swift language mode 6 and macOS/SDK 27+. Read README.md, CONTRIBUTING.md and the relevant contracts in Documentation/Architecture before changing behavior. -- Keep nine public products: JSON, JSONSchema, JSONStreaming, JSONPatch, JSONHTML, - JSONBridges, JSONNavigation, JSONInspection and the swift-json executable. JSONFilterRuntime and JSONCLIEngine are package-private. +- Keep ten candidate products: JSON, JSONSchema, JSONSchemaExamples, JSONStreaming, + JSONPatch, JSONHTML, JSONBridges, JSONNavigation, JSONInspection and the swift-json executable. + JSONSchemaExamples is opt-in and depends on JSON and JSONSchema only. + JSONFilterRuntime and JSONCLIEngine are package-private. JSONHTML and opt-in JSONBridges depend on JSON only; core never re-exports the bridges. The approved CLI and E2 additions preserve historical product contracts. JSONNavigation depends on JSON only. Opt-in JSONInspection depends on JSON and diff --git a/Documentation/Architecture/API.md b/Documentation/Architecture/API.md index 55b5292..fb8fd0e 100644 --- a/Documentation/Architecture/API.md +++ b/Documentation/Architecture/API.md @@ -1,7 +1,7 @@ # Public API contract This document describes the implemented public interface. Native compiler symbol -graphs supply the complete declarations in the seven DocC catalogs. Examples +graphs supply the complete declarations in the eight DocC catalogs. Examples in the [README](../../README.md) are compiled and executed by the public consumer. ## Products and ownership @@ -18,7 +18,8 @@ in the [README](../../README.md) are compiled and executed by the public consume | JSONHTML | JSONHTMLRenderer, JSONHTMLRenderingOptions, JSONHTMLLimits, JSONHTMLRenderReport, JSONHTMLAnnotation, JSONExportDocument, JSONExportOptions, JSONExportReport | Structured forms/cards and bounded native previews, exact HTML, native Apple transfer, immutable prepared JSON/text/Markdown/static HTML/opt-in styled RTF and cumulative budgets | JSON imports no siblings. JSONSchema depends on JSON and package-private -JSONRegexRuntime. Streaming, Patch, HTML and Bridges depend on JSON. There are eight candidate public products: seven libraries and the swift-json executable, +JSONRegexRuntime. Streaming, Patch, HTML, Bridges and Navigation depend on JSON. +JSONSchemaExamples depends only on JSON and JSONSchema. There are nine public products: eight libraries and the swift-json executable, and no external root-package dependencies. No HTTP, MCP or documentation-tool dependency is introduced. JSONHTML conditionally uses Apple SDK frameworks for explicit native transfer/PDF preparation; the portable value, schema, Patch and streaming contracts do not acquire UI semantics. @@ -233,13 +234,53 @@ is neither native validation of an instance nor a service-acceptance guarantee. and all phase accounting are specified in [Providers](Release1.4/Providers.md) and the frozen [profile table](Release1.4/ProviderProfiles.json). No new product, SDK client or Apple type is added. -## Best-effort diagnostics and versioned CLI identity - -With best-effort schema output, an optional diagnostic-byte refusal returns ordinary -invalidity with `diagnosticsComplete == false`; it does not poison the active work -ledger. Work, retention, annotation exhaustion and cancellation remain operational -errors. Strict diagnostic output still throws on the same byte bound. - +## Schema examples (1.6 G4 development) + +The optional JSONSchemaExamples product prepares an immutable document/explicit +registry/compiled-plan snapshot. `JSONSchemaExampleGenerator(document:registry:options:)` +and `generate(seed:profile:)` offer exact declared candidates and compiler-qualified +static reference targets. Every success contains the actual native valid verdict +for the original root. Minimal scans stable exact declarations; demonstration +uses metered SplitMix64 rejection sampling and circular scans. An immutable exact +`JSONSchemaExampleCorpus` supplies candidates for true/empty schemas. Returned +objects/numbers use scalar order and normalized exact spelling. Conservative +typed construction covers the qualified foundational subset. G4 adds conservative +contains minima and a small anchored ASCII pattern-proposal grammar. Native +validation retains conditional, dependency, regex and evaluated-location semantics. +General constraint +solving, arbitrary regex inversion and dynamic construction remain incomplete. + +`JSONSchemaExampleFailure` distinguishes unsupported construction, searchExhausted, +reviewed trivial provenUnsatisfiable and invalidConfiguration. Core operational +failures and cancellation propagate without retries. Preparation accounting is +separate from the shared per-generation ledger, which includes all completed +invalid checks. Discarded validation ownership is released without refunding work. Logical +retention is not process RSS. +The result binds the versioned snapshot, ordered corpus, all generation/compiler/ +evaluator caps and flags, and complete abstract trace with SHA-256 digests. Trace +bytes share diagnosticBytes with native diagnostics; exhaustion never truncates a +success. See [the G3 constructive subset](Release1.6/G3Construction.md), +[the G4 advanced boundary](Release1.6/G4Construction.md), +[the capability ledger](Release1.6/Capabilities.json), [the G2 byte transports](Release1.6/G2Determinism.md), the historical +[G1 API decision](Release1.6/G1API.md), and the +[compiled public consumer](../../Examples/SchemaExamplesConsumer/README.md). + +## Optional verified schema examples (1.6 development) + +JSONSchemaExamples adds an immutable prepared generator over admitted documents and +explicit offline registries. Every returned value has a completed native root verdict, +with exact JSON semantics, cumulative generation statistics and versioned provenance. +It does not alter JSON/JSONSchema APIs, policies, products or dependencies. + +See the [current public declarations and failures](Release1.6/API.md), +[supported construction subset](../../Sources/JSONSchemaExamples/Documentation.docc/TypedConstruction.md), +[resource scope](Release1.6/SchemaExamplesLimits.md) and +[compiled public consumer](../../Examples/SchemaExamplesConsumer/README.md). +Preparation and generation use separate finite ledgers. All attempts within a generation +share one ledger; false verdicts consume work, operational errors/cancellation terminate +without a candidate or successful report, and search exhaustion is not a proof. +The [combined-source G8 record](Release1.6/G8Qualification.md) describes qualification, +review and platform limits. Merge and release approval remain separate. The CLI's human version reports the candidate/release source version. JSON metadata includes `revisionKind: "source-sha256"` and `revision: "sha256:"`. This is the SHA-256 of the ordered Package.swift plus Sources content manifest, excluding only diff --git a/Documentation/Architecture/ExtractionManifest.json b/Documentation/Architecture/ExtractionManifest.json index 9d3e9ff..0956116 100644 --- a/Documentation/Architecture/ExtractionManifest.json +++ b/Documentation/Architecture/ExtractionManifest.json @@ -249,6 +249,28 @@ "Scripts/qualify_schema_providers.py", "Tools/CLIQualification/Sources/ProviderConsumer" ] + }, + { + "repository": "SwiftJSON-SchemaExamples-S3-Codex-Wave-v1.0 briefing packet", + "archive_sha256": "f67ec2f5d0bfd223f54a3577f93c03951a7992ecab524910ba44f6bc8621c4a1", + "original_path": "Reference/SplitMix64.swift and Reference/SplitMix64Check.swift", + "destination": "Sources/JSONSchemaExamples/SchemaExampleDeterminism.swift", + "disposition": "SplitMix64 constants, wrapping steps and rejection threshold adapted into invocation-owned noncopyable state with cumulative native metering; standalone shims not used. Sixteen golden draws retained in real-engine tests.", + "attribution": "Apache-2.0 SPDX header retained; original Swift-JSON integration code Apache-2.0.", + "validation": "Tests/JSONSchemaExamplesTests/DeterminismTests.swift; independent stdlib transport oracle and public consumer", + "evidence": "Release1.6 G2 qualification; actual native/safety/docs receipts" + }, + { + "repository": "SwiftJSON-SchemaExamples-S3-Codex-Wave-v1.0 briefing packet", + "archive_sha256": "f67ec2f5d0bfd223f54a3577f93c03951a7992ecab524910ba44f6bc8621c4a1", + "original_path": "Fixtures/cases.json", + "original_sha256": "6f2bfd4fdb7c7e206b6570c2156ca8e248831840733b0258aa8cd5938b35649f", + "destination": "Tests/Fixtures/SchemaExamples", + "disposition": "Original 44-case input retained byte-for-byte alongside the independently reviewed G0 integral-number correction (155 witnesses). G3 selects exactly 24 must-generate IDs plus two sound proofs; 18 G4 cases remain separate. No fixture expected verdict was lowered for the generator.", + "amended_sha256": "5b3e6e40e83439a810d0eb7ac412ce158a4e582f36b9d36d6e313c159654ddd3", + "attribution": "User-authored packet fixture input; original integration/tests Apache-2.0. Packet source/provenance retained separately.", + "validation": "TypedConstructionTests.swift and public G3Acceptance consumer; strict native compiler/evaluator authority", + "evidence": "Release1.6/G3 qualification and materialized per-case receipts" } ], "original_implementation_notes": [ @@ -272,10 +294,14 @@ "1.4 C4 immutable IR/function/pattern metadata, invocation closures/thunks, continuations, scalar string and format helpers, root-provenance paths and transactional mutation are original Apache-2.0 code. No eager upstream evaluator, Foundation regex or approximate arithmetic was copied.", "1.4 C5 stream/offline-schema adapters, checked sink handoff, command help and consumers are original Apache-2.0 code. Five NewInputs fixtures are copied byte-for-byte from the maintainer CLI briefing pack 43f8819c57e3d93bc6f7cf561d2d111442e089f2973cb7c62e16e3bd3fd73d0d; these authored data fixtures introduce no runtime dependency. Missing provider helper prerequisites remain blocked rather than adapted or silently rewritten.", "1.4 checked schema resource lookup and its missing/quota/cancellation/install regressions are original Apache-2.0 code. Supported app/framework/command layouts retain all nine official resource pins; no unbounded directory scan, fallback engine or external dependency is introduced.", + "1.6 S3 G4 contains-minimum proposals and the small anchored ASCII proposal scanner are original Apache-2.0 Swift code. Native compiler-qualified references and complete native regex/schema evaluation remain authoritative; no matcher, resolver, engine source or dependency is adapted. The original/amended S3 fixtures retain their pinned byte identities.", "1.5.1 restores 126 original Apache-2.0 test functions and qualification tools from Swift-JSON 1.2.1 (e8b687f822c577311156b13bf1d15c2d5620b84e), with file hashes and stable function identities in Qualification/Release1.5.1/Restoration.json. The diagnostic omission and Git-free provenance repairs are restored original code; current seven-product harness reconciliation and deterministic CLI identity are original contributions. No new upstream adaptation or dependency.", + "1.6 S3 G7 seeded grammar, scalar-key permutation tests, public replay consumer and strict cardinality/matrix harness are original Apache-2.0 contributions. The 64-schema authored fixture has SHA-256 cb55604183a6db7df5fa968db283e7abeae794277d2b0cef033b0bdb92e615cb. Independent python-jsonschema 4.26.0 is research qualification only, with exact numeric binding; no Python runtime dependency or copied validator/engine is distributed.", + "1.6 S3 G8 materializes the unchanged synthetic corpus per caller access, replacing process-lifetime stored JSON payloads exposed by Linux ASan/LSan. The public process-lifetime consumer and strengthened exact/value-copy test are original Apache-2.0 qualification code; no suppression, runtime fallback, new dependency or corpus/digest change is introduced.", "Composition reference-load fixtures, serial descriptor staging, public-only adapter, capacity/terminal tests and strict CI receipt checks are original Apache-2.0 code. Generated JSON graphs introduce no upstream protocol fixtures, engine or dependency. Core public APIs, finite profiles and accounting weights are unchanged.", "NAV5 public array-evidence packaging, external consumer and DocC are original Apache-2.0 code. Existing exact shape evidence, exclusive reducer, weights and I01-I17 semantics are unchanged; no upstream engine or dependency is adapted. Promotion is a draft candidate, not a released product.", - "NAV5 package-only complete structural snapshot, parent-linked metadata, original source-ownership transfer and S01-S24 synthetic tests are original Apache-2.0 code. The existing read scope admits once; its default public read/traversal/inspection completion behavior is preserved. No persistent identity, upstream algorithm or new dependency is introduced." + "NAV5 package-only complete structural snapshot, parent-linked metadata, original source-ownership transfer and S01-S24 synthetic tests are original Apache-2.0 code. The existing read scope admits once; its default public read/traversal/inspection completion behavior is preserved. No persistent identity, upstream algorithm or new dependency is introduced.", + "NAV6 snapshot access-level promotion, external consumer, independent 28-record maintainer oracle, terminal-transfer/concurrent lookup proofs and compiled DocC are original Apache-2.0 contributions. No upstream engine, new semantic authority, dependency or additional product is adapted." ], "jsonhtml_export_original_contributions": { "license": "Apache-2.0", diff --git a/Documentation/Architecture/Release1.6/API.md b/Documentation/Architecture/Release1.6/API.md new file mode 100644 index 0000000..39377f9 --- /dev/null +++ b/Documentation/Architecture/Release1.6/API.md @@ -0,0 +1,112 @@ +# JSONSchemaExamples current public contract + +This is the current S3 interface through G6, superseding the provisional G1 API +notes where later accepted increments differ. Historical decisions and receipts +are preserved. It is an opt-in 1.6 development product; G7/G8 and main repair +integration remain open. Native compiler symbol graphs and source declarations +are the API authority. There is no public mutable plan, evaluator, meter or result +initializer, and no new root-package dependency. + +## Prepare, generate and inspect + +| API | Actual role and defaults | +| --- | --- | +| `JSONSchemaExampleGenerator(document:registry:options:) throws` | Admitted JSONSchemaDocument, explicit registry default `.empty`, options default `.init()`; compile/normalize/hash once under one preparation ledger | +| `generate(seed:profile:) throws -> JSONSchemaExampleResult` | Seed UInt64 default 0; profile default `.minimal`; independent cumulative invocation; returns only after native valid verdict and complete trace/hash | +| `document`, `registry`, `options` | Immutable original preparation inputs/policies; no inferred format/default/registry behavior | +| `preparationStatistics` | Separate compilation-domain counts, including snapshot/corpus/policy work; not part of the generation report | +| `sourceSnapshotDigest`, `corpusDigest`, `policyDigest` | Lowercase SHA-256 identities of the documented separate transports | + +The generator is checked Sendable. Mutable construction frames, PRNG, trace and +validation state belong to one synchronous call. The compiled root is private and +retained immutable. Caller document/registry/corpus construction precedes preparation. +Returned values have normalized exact numbers, scalar-sorted object insertion order +and preserved array nesting; mathematical/scalar semantics are those of JSON. + +## Options and profiles + +`JSONSchemaExampleOptions(limits:maximumAttempts:corpus:compilationOptions:validationOptions:)` +uses balanced generation limits, maximumAttempts **16**, corpus **.synthetic** and +native JSONSchemaOptions defaults for both compilation and validation. Options are +values checked when preparing a generator. Changing a caller's options afterward +does not mutate an existing preparation. + +* Generation limits intersect validation limits per metric. All 49 entries remain + finite and contribute to policy identity. See [limits](SchemaExamplesLimits.md). +* Compilation options remain separate, including any format assertion selected at + compilation. Validation output, diagnostic policy, annotations and assertion + settings are forwarded unchanged. Required vocabulary assertions still apply. +* `maximumAttempts` must be positive and strictly below UInt64.max. It bounds + completed candidate validations, including false verdicts, without promising + that every search reaches the limit or finds an existing witness. +* `.minimal` uses stable declared order and conservative small typed proposals, + with no random draws. It is not a global minimization guarantee. +* `.demonstration` uses seeded SplitMix64 candidate/corpus choices. Some inputs + produce the same value for every seed. Every raw rejected draw is charged. +* `JSONSchemaExampleCorpus(values:)` stores exact ordered caller JSON without + admission; preparation checks/normalizes it and rejects an empty corpus. The + synthetic ordered values are null, false, 0, "example", [], {}. The read-only + `synthetic` property materializes these values per access; caller lifetime owns + their storage, before checked preparation. Corpora are hints. + +Candidate role order is **const, default, enum, examples** (ASCII keyword order). +Array order is semantic. Discovery follows qualified alternatives/static edges; +typed construction then offers bounded variations. Defaults and examples can be +invalid; nothing is inserted or repaired. Complete original-root validation alone +establishes success. See [supported subset](../../../Sources/JSONSchemaExamples/Documentation.docc/TypedConstruction.md) +and [all 59 keyword classifications](Capabilities.json). General satisfiability, +regex inversion, triggered dependency/key synthesis and constructive dynamic scope +are incomplete. No boundary profile, AI, online resolver or interop converter is added. + +## Result and provenance + +JSONSchemaExampleResult exposes immutable `value`, actual native `validation`, +`seed`, `profile`, `attempts`, `statistics`, `validationOptions` and +`compilationOptions`, plus these identities: + +| Properties | Current identity | +| --- | --- | +| generatorVersion | s3-candidates-v4 | +| schemaDigestAlgorithm / sourceSnapshotDigest | swift-json-schema-snapshot-v1 / source and ordered registry hash | +| corpusDigestAlgorithm / corpusDigest | swift-json-example-corpus-v1 / admitted ordered corpus hash | +| policyDigestAlgorithm / policyDigest | swift-json-example-policy-v1 / all three 49-cap profiles, attempt ceiling and policy flags | +| rngAlgorithm / randomDraws | splitmix64-v1 / every raw draw, including rejection | +| traceDigestAlgorithm / traceDigest / traceEventCount | swift-json-example-trace-v2 / complete abstract decision hash and event count | + +Transports exclude source maps, unavailable original source bytes and caller time. +They are not RFC8785/JCS. Registry order contributes to source identity. Seed alone +cannot bind a result; retain implementation commit, versions and all input identities. +[G2 transports](G2Determinism.md), [G3 trace additions](G3Construction.md) and the +[G5 accounting correction](G5Resources.md) preserve historical versions separately. +Statistics snapshot certification and conservative staging ownership, before transfer; +logical retained bytes/peaks are not heap size or RSS. WorkUnits includes subsystem work. + +## Failures and semantic authority + +| Failure | Meaning | +| --- | --- | +| unsupportedFeature(candidateConstruction / dynamicReference / cyclicReference) | Current constructor lacks a proposal or reached unsupported scope/active cycle; no validity conclusion | +| searchExhausted(attempts:) | All offered candidates rejected or finite ceiling reached; not an unsatisfiability proof | +| provenUnsatisfiable(falseSchema / notTrue) | Only reviewed trivial root false or active root `not` of true/empty schema | +| invalidConfiguration(attemptLimit / emptyCorpus) | Invalid generator configuration before preparation/search | +| Native compilation/evaluation/resource error or CancellationError | Operational failure; terminal, never ordinary false validation or accepted incompleteness | + +No failed call publishes a value, complete trace or successful work report. A caught +operational error cannot resume its internal invocation; a later public call is an +independent operation. Immutable preparation remains reusable. Public consumers must +not invent failed-work statistics when composing separate calls. + +## Compiled examples and integration + +[README examples](../../../README.md#generate-verified-schema-examples-16-development) +compile in the seven-library consumer. Every Swift block in all five S3 DocC guides +is synchronized with DocumentationExamples.swift and runs twice in the separate +[public consumer](../../../Examples/SchemaExamplesConsumer/README.md), alongside +15 preserved cases and two compiler negatives. Drift/missing snippets fail the lane. +Both packages import public products only; neither is a root runtime dependency. + +JSONSchemaDSL is absent from the inspected S3 Package.swift and main base. Its +independent train is untouched. A DSL equality example is deferred until a real +product is available during integration. An admitted document from any authoring +source can use this API under the same native dialect; no foreign notation is +implicitly converted. CLI, JSONHTML, AI and schema interop gain no S3 dependency. diff --git a/Documentation/Architecture/Release1.6/Capabilities.json b/Documentation/Architecture/Release1.6/Capabilities.json new file mode 100644 index 0000000..2b3e2b9 --- /dev/null +++ b/Documentation/Architecture/Release1.6/Capabilities.json @@ -0,0 +1,329 @@ +{ + "schema_version": 1, + "gate": "S3-G4", + "generator_algorithm": "s3-candidates-v4", + "trace_algorithm": "swift-json-example-trace-v2", + "semantic_authority": "Original compiled root plus completed JSONSchemaValidator result. Capability classifications describe proposal construction only.", + "groups": [ + { + "id": "type", + "keywords": [ + "type" + ], + "construction": "constructive", + "policy": "Fixed type preference, unions/intersections from active vocabularies; integer remains exact integral number.", + "evidence_tests": [ + "g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds", + "g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes" + ] + }, + { + "id": "declared-candidates", + "keywords": [ + "const", + "enum", + "default", + "examples" + ], + "construction": "conservative", + "policy": "Exact declared candidates; defaults/examples are hints, nested const/enum proposals still require complete root validation.", + "evidence_tests": [ + "g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds", + "g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes" + ] + }, + { + "id": "numeric", + "keywords": [ + "minimum", + "maximum", + "exclusiveMinimum", + "exclusiveMaximum", + "multipleOf" + ], + "construction": "conservative", + "policy": "Exact bounded decimal step/rounding; dense alignment can exhaust finite digit/work limits. Multiple conjunctive divisors are certified natively rather than solved generally.", + "evidence_tests": [ + "g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds", + "g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes" + ] + }, + { + "id": "strings", + "keywords": [ + "minLength", + "maxLength" + ], + "construction": "constructive", + "policy": "Unicode-scalar lengths; bounded admitted corpus/filler tokens.", + "evidence_tests": [ + "g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds", + "g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes" + ] + }, + { + "id": "format", + "keywords": [ + "format" + ], + "construction": "conservative", + "policy": "Fixed email token when native assertion applies; annotation does not assert or imply a type. Other formats are native certification only.", + "evidence_tests": [ + "g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds", + "g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes" + ] + }, + { + "id": "objects", + "keywords": [ + "properties", + "required", + "additionalProperties", + "minProperties", + "maxProperties" + ], + "construction": "conservative", + "policy": "Union of exact required keys, every conjunct scoped separately, optional omission and finite declared/synthetic fill names.", + "evidence_tests": [ + "g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds", + "g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes" + ] + }, + { + "id": "arrays", + "keywords": [ + "prefixItems", + "items", + "minItems", + "maxItems" + ], + "construction": "constructive", + "policy": "Explicit iterative frames, checked lengths and positional indices; nested arrays do not flatten.", + "evidence_tests": [ + "g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds", + "g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes" + ] + }, + { + "id": "uniqueness", + "keywords": [ + "uniqueItems" + ], + "construction": "conservative", + "policy": "Finite leaf variations; only native exact deep comparison certifies uniqueness. No complete cardinality solver.", + "evidence_tests": [ + "g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds", + "g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes" + ] + }, + { + "id": "composition", + "keywords": [ + "allOf", + "anyOf", + "oneOf" + ], + "construction": "conservative", + "policy": "Positive conjunction hints and finite alternative child proposals. No schema merge or branch-local success. Native exactly-one/conditional/annotation semantics remain authoritative.", + "evidence_tests": [ + "g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds", + "g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes" + ] + }, + { + "id": "static-resources", + "keywords": [ + "$ref", + "$id", + "$schema", + "$anchor", + "$defs", + "definitions", + "dependencies", + "$vocabulary", + "$comment" + ], + "construction": "compiler_authority", + "policy": "Compiler-qualified resource/node handles; no URI guessing or implicit definition rooting. Repeated static targets are DAG sharing; same-instance ancestor cycles are unsupported. Legacy definitions/dependencies remain non-applicative containers whose referenced children retain compiler locations.", + "evidence_tests": [ + "g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds", + "g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes" + ] + }, + { + "id": "dynamic", + "keywords": [ + "$dynamicRef", + "$dynamicAnchor" + ], + "construction": "unsupported_dynamic_construction", + "policy": "A reached scope-dependent reference fails explicitly. Omitting an optional dynamic child may produce a native-certified finite witness; that does not qualify constructive dynamic-scope navigation.", + "evidence_tests": [ + "g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds", + "g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes" + ] + }, + { + "id": "native-only", + "keywords": [ + "patternProperties", + "propertyNames", + "dependentRequired", + "dependentSchemas", + "unevaluatedProperties", + "unevaluatedItems", + "if", + "then", + "else" + ], + "construction": "native_certification_only", + "policy": "No constructive interpretation of these keywords. Optional omissions and finite whole-root proposals can satisfy the pinned fixtures; only complete native evaluation establishes conditionals, dependencies, key patterns/names and evaluated locations. No general triggered-constraint solver.", + "evidence_tests": [ + "g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds", + "g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes" + ] + }, + { + "id": "not", + "keywords": [ + "not" + ], + "construction": "native_certification_only", + "policy": "Only active root not-true/not-empty has a reviewed trivial unsatisfiability proof; other negations are native certification only.", + "evidence_tests": [ + "g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds", + "g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes" + ] + }, + { + "id": "annotations", + "keywords": [ + "title", + "description", + "deprecated", + "readOnly", + "writeOnly", + "contentEncoding", + "contentMediaType", + "contentSchema" + ], + "construction": "metadata_only", + "policy": "Never executed, inserted, repaired, resolved online or treated as validation authority.", + "evidence_tests": [ + "g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds", + "g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes" + ] + }, + { + "id": "pattern-proposals", + "keywords": [ + "pattern" + ], + "construction": "conservative", + "policy": "Fully anchored printable ASCII literals, escaped regex punctuation, single ASCII-range classes and exact repetition only. Never a regex matcher; unsupported syntax may use finite corpus/filler proposals. Native regex compilation/matching and quotas remain authoritative.", + "evidence_tests": [ + "g4AnchoredLiteralRangeAndExactRepeatProposalsAreNativeCertified", + "g4UnsupportedRegexSyntaxNeverPretendsToBeAnInverter", + "g4RegexOperationalFailureIsTerminalRatherThanAnInvalidCandidate", + "g4PatternScannerHasExactWorkAndPreallocationBoundaries", + "g4CancellationDuringPatternScanningLatchesTheInvocation" + ] + }, + { + "id": "contains-proposals", + "keywords": [ + "contains", + "minContains", + "maxContains" + ], + "construction": "conservative", + "policy": "Minimum positive-match proposals combined with prefix/items constraints per conjunct. Native validation counts all matches/maxima and annotations. Disjoint partitions and excluding surplus matches are not solved.", + "evidence_tests": [ + "g4ContainsDefaultZeroAndInactiveSiblingLimitsRemainDistinct", + "g4ContainsPrefixAndUniqueConstraintsShareTheRootCertificate", + "g4ExtraContainsMatchesRemainHonestSearchExhaustion", + "g4ContainsCardinalityIsCheckedBeforeItemStaging" + ] + } + ], + "fixture_matrix": { + "source": "Tests/Fixtures/SchemaExamples/S3-integral-correction-v1.json", + "sha256": "5b3e6e40e83439a810d0eb7ac412ce158a4e582f36b9d36d6e313c159654ddd3", + "g3_must_generate_ids": [ + "boolean-true", + "null-kind", + "string-length", + "exact-integer-beyond-2pow53", + "exact-decimal-multiple-of", + "numeric-exclusive-near-zero", + "object-required-and-null", + "unicode-exact-scalar-keys", + "undeclared-required-key", + "object-min-max-properties", + "array-prefix-items", + "array-unique", + "enum", + "const-exact-decimal", + "anyof", + "allof-object-composition", + "local-ref", + "format-annotation", + "format-assertion", + "invalid-default-annotation", + "invalid-examples-annotation", + "false-nested-schema", + "object-number-key-strings", + "required-with-additional-properties-false" + ], + "profiles": [ + "minimal", + "demonstration" + ], + "seeds": [ + "0", + "1", + "42", + "18446744073709551615" + ], + "expected_successful_invocations_per_process": 192, + "g3_proof_ids": [ + "boolean-false", + "not-true" + ], + "g4_cases": 18, + "g4_status": "passed native increment; full release/platform gates remain open", + "g4_must_generate_ids": [ + "array-contains", + "condition-then-else", + "dependent-required", + "dependent-schema", + "pattern-properties", + "property-names", + "unevaluated-properties-composition", + "simple-regex", + "mincontains-with-prefixitems", + "array-mixed-evaluation" + ], + "g4_may_fail_ids": [ + "oneof-overlap", + "recursive-tree", + "dynamic-ref", + "conflicting-allof", + "unfulfillable-unique-enum", + "external-offline-ref", + "not-string", + "oneof-overlap-limited" + ], + "g4_expected_required_successes_per_process": 80, + "g4_total_invocations_per_process": 144, + "all_native_witness_expectations": 155, + "g4_observed_successes_per_process": 112, + "g4_observed_explicit_incomplete_per_process": 32, + "g4_public_processes": 2 + }, + "limitations": [ + "No general satisfiability decision or universal must-generate promise outside the pinned subset", + "No unqualified cross-platform, RSS or controlled-throughput claim", + "Main 1.5.1 integration and G7/G8 remain separate gates" + ] +} diff --git a/Documentation/Architecture/Release1.6/Decisions.md b/Documentation/Architecture/Release1.6/Decisions.md new file mode 100644 index 0000000..718313d --- /dev/null +++ b/Documentation/Architecture/Release1.6/Decisions.md @@ -0,0 +1,60 @@ +# S3 initial decisions + +These decisions establish the implementation boundary. Exact new public declarations +remain subject to G1 real-engine compiler/consumer qualification. + +1. Prepare from an admitted JSONSchemaDocument, explicit immutable registry and + separately recorded compilation/evaluation options. Retain the compiled plan + for every final verdict. A public compiled-plan-only initializer would lose + construction/reference snapshot obligations and is deferred. +2. Use a minimal package-only JSONSchema view over compiler-resolved node/resource + identities and static edges. Existing node fields are internal. Do not make the + whole graph public/package-mutable, import the regex runtime into the generator, + duplicate URI resolution or infer targets from names. Dynamic construction is + scope-aware or explicitly unsupported; native evaluation remains authoritative. +3. Reuse the existing package validation entry with the same noncopyable + JSONWorkMeter through the entire generate invocation. Existing operation-domain + switching preserves consumed work and terminal health. Construction, numeric + work, references, rejected draws and completed false validations all consume + cumulative quotas. G5 must prove this behavior; G0 typechecking alone does not. +4. Keep preparation separately bounded. The existing package compiler entry can + borrow a preparation meter; normalized snapshot serialization and SHA-256 must + share its owned work/retention boundary. Caller-created document/registry work + precedes preparation. Do not add independent memory peaks or label them RSS. +5. Numeric synthesis uses bounded exact components/decimal routines. No dependency + on the private CLI/filter targets merely to obtain arithmetic, and no Double, + epsilon, Foundation Decimal or NSNumber authority. Any factored helper requires + review and regression coverage without changing public arithmetic semantics. +6. Retain splitmix64-v1 wrapping arithmetic only for PRNG state/mixing. Quota + counters use checked arithmetic. Every rejected uniform-selection draw is billed. + The standalone reference's native vectors are preparation evidence, not a + qualification of unmetered reference code as production runtime. +7. Specify SwiftJSON-schema-snapshot-v1 before freezing goldens: compact scalar-key + normalized-number writer bytes, unambiguous length-prefixed resource identities, + explicit ordering and separately identified policies/corpus. Original source + bytes cannot be invented from a schema document/source map. No RFC8785 claim. +8. Return a certified result only through a completed native valid verdict. Keep + searchExhausted, unsupported, sound false/not-true proofs and invalid configuration + distinct. Propagate cancellation and operational evaluator failures terminally. + Defaults/examples can supply candidates only; no repair or inferred requiredness. +9. Adopt the explicitly documented integer-fixture amendment and extra fractional + negative, retaining the original packet. Python exists only in the isolated + research/oracle environment. It never becomes a Swift runtime dependency. +10. Add one optional library product and its tests during G1; retain all current + products and private targets. The independent Dual DSL train does not gate S3. + Update exact CI discovery/manifest/DocC checks only alongside real additions; + preserve all 49 core probes and unchanged pinned official suite policies. + +Full generation accounting, cross-platform deterministic goldens, cancellation +behavior and support classifications remain unqualified until their gates execute. +Finite candidate search does not decide arbitrary schema satisfiability. Profiles +are preferences, with no globally minimal witness or universal generation guarantee. + +G5 closes decision 3 for the qualified native increment. The existing package-only +compiler/evaluator meter bridge is sufficient; no new public accumulator or error +report API is added. A generator-internal borrowed entry is the actual public +execution path and allows deterministic terminal-state/cleanup qualification. +Conservative staging reservations live through proposal/invocation boundaries; +reports snapshot certification before result ownership transfers. See G5Resources.md. +G5 removes an unmatched 32-byte trace refund without changing logical weights, +candidate choices or transport versions. Later full/platform gates remain open. diff --git a/Documentation/Architecture/Release1.6/G1API.md b/Documentation/Architecture/Release1.6/G1API.md new file mode 100644 index 0000000..dadca7c --- /dev/null +++ b/Documentation/Architecture/Release1.6/G1API.md @@ -0,0 +1,83 @@ +# G1 API and preparation decision + +Accepted before implementation on `codex/1.6-schema-examples`, base `507dbf9`. +This is a working candidate-verification increment, not the G3 constructive subset. + +The optional eighth product `JSONSchemaExamples` imports JSON and JSONSchema only. +Existing product dependencies remain unchanged. Its immutable Sendable generator +accepts `document:registry:options:`, compiles once and retains that exact offline +plan. Each `generate(seed:profile:)` invocation owns one noncopyable core meter, +intersecting generation and validation limits without changing evaluator policies. +Preparation has its own compilation meter, shared with snapshot serialization and +hashing. Caller document/registry admission precedes preparation. + +G1 accepts these public roles: + +- `JSONSchemaExampleOptions(limits:maximumAttempts:compilationOptions:validationOptions:)`. + Use existing JSONLimits rather than a second copy of its 49 metrics. A positive + finite maximumAttempts is a separate search ceiling; zero/UInt64.max are invalid. +- `JSONSchemaExampleGenerator(document:registry:options:) throws`, and + `generate(seed:profile:) throws -> JSONSchemaExampleResult`. The only current + profile is `.minimal`. Seed is recorded; G1 makes no random choices. No RNG or + corpus API is advertised before G2's algorithms are implemented. +- An immutable result containing the exact value, actual completed validation, + seed/profile, implementation version, source snapshot digest, evaluation policy, + attempt count and cumulative invocation statistics. Result initialization is + internal; callers cannot construct a purported certification through this API. +- Typed failure cases `unsupportedFeature`, `searchExhausted`, + `provenUnsatisfiable`, `invalidConfiguration`, with finite enum codes instead + of untrusted free-form messages/paths. Core schema/resource/configuration errors + and CancellationError propagate unchanged. + +Candidate order is active const, enum in declaration order, default, examples in +declaration order, then compiler-resolved static references in compiler edge order. +Boolean true and empty object schemas propose null. Each candidate is checked +against the **original prepared root**, including reference siblings. Metadata is +never inserted or repaired. Every completed false check consumes the same ledger; +operational errors terminate immediately. Discarded validation ownership is released without refunding work. Statistics +use existing logical weights; they are not RSS or allocator measurements. +Unsupported construction and reference cycles remain explicit failures. Dynamic +edges are not followed; encountering one in the searched declaration is unsupported. + +Only root false and root not-true/not-empty-object with an active applicator +vocabulary have G1 unsatisfiability proofs. No reference-cycle, invalid-candidate +or search-limit failure is presented as a proof. Other satisfiable schemas can +still fail to produce a witness until later gates. + +The package-only `JSONSchemaQualifiedNode` handle is created only by a compiled +plan or its checked child/reference edges. Its immutable view exposes raw +declaration, compiler resource URI, original document Pointer, active vocabulary +and format-assertion flag. Indexed edge inspection charges the borrowed meter. +It exposes neither mutable graph fields nor regex programs. Integer identities are +local to one plan. No new public JSONSchema declarations or URI resolver are added. +An internal compiler overload borrows the preparation meter while preserving the +original admitted document/source map, with the same compilation implementation. + +`swift-json-schema-snapshot-g1-v1` is SHA-256 over this exact transport: ASCII +`SwiftJSON-schema-snapshot-g1-v1\n`, big-endian UInt64 document count (root then +registry documents in explicit caller order), then for each document a one-byte +retrieval-URI-present marker, a length-prefixed UTF-8 retrieval URI (empty if absent), +and length-prefixed compact JSONWriter bytes (scalar key order, normalized exact +numbers). Every length is big-endian UInt64. Source maps/original lexemes are not +source text and are excluded. Registry order is deliberately part of this identity. +Policies are retained separately, unchanged; equal source digest does not imply +equal policies. This is not RFC8785, raw-source hashing or G2's final corpus/trace +transport. The G1 algorithm ID is `s3-g1-candidates-v1`. + +Rejected for G1: public graph access, caller-supplied compiled plans without a +coherent source/registry snapshot, URI guessing, opaque validation reports, broad +arithmetic/regex synthesis, unimplemented profiles, invented corpus/trace receipts, +and any dependency on the separate DSL train. Full G5 lifecycle qualification and +G7 platform/conformance qualification remain separate gates. + +## G1 resource units and review + +Each newly searched compiled declaration charges schemaNodes, rather than treating +visited identities as active evaluator frames. Pending construction handles are +bounded by evaluationFrames. Identity slots and pending handles use the existing +indexSlot (32) and frame (16) logical weights; a returned certificate reserves +128 logical bytes for its metadata. Candidate admission/evaluation uses the native +weights. Failed completed validations release their newly owned result/scratch +retention without refunding work, diagnostics or lifetime value-admission counts. +No independent peaks are added to impersonate live retention or RSS. The G1 tests +include a zero schemaNodes discovery ceiling and a shared-work exact boundary. diff --git a/Documentation/Architecture/Release1.6/G1Qualification.md b/Documentation/Architecture/Release1.6/G1Qualification.md new file mode 100644 index 0000000..1401490 --- /dev/null +++ b/Documentation/Architecture/Release1.6/G1Qualification.md @@ -0,0 +1,109 @@ +# S3 G1 implementation and native qualification + +G1 is complete on the isolated `codex/1.6-schema-examples` branch. This closes the +public API/preparation increment, not G2–G8 or a 1.6 release gate. The implementation +commit is `6c19580dec99c9363bb4b4d1611af8b8a2bab266`; the following closure adds +maintainer records and strengthens the public consumer, without changing library +or test source. Native receipts record tested source hashes as well as Git identity. + +## Accepted implementation + +`JSONSchemaExamples` is the eighth public product and seventh library. It depends +only on JSON and JSONSchema; existing product edges and the empty root dependency +graph are unchanged. No sibling DSL, CLI, platform, resolver or regex implementation +was added. Original code is Apache-2.0, with no new upstream code adaptation. + +The package-only qualified-node handle exposes immutable declarations, active +vocabulary/format policy and checked compiler-resolved child/static-reference +edges. It exposes no mutable graph arrays, URI resolver or regex engine. Its +targets originate only in the retained plan. External access and external result +construction are rejected by actual compiler probes. + +The public generator compiles once from document plus explicit registry, retaining +separate compilation and validation policies. G1 offers const, enum, default, +examples, true/empty roots and static references. Each candidate is checked against +the original root. The successful result contains the native completed valid +verdict. Unsupported construction, bounded exhaustion, two sound trivial proofs +and generator configuration errors are distinct. Core errors/cancellation escape +terminally; no failed operation is retried with a new budget. + +Preparation shares one meter across compilation, normalized snapshot serialization +and portable SHA-256. Each generation shares one noncopyable meter across discovery +and every completed validation, including false verdicts. Discarded validation +ownership is released; work and lifetime admissions are not refunded. Counts and +peak logical bytes are neither caller instruction quotas nor process RSS. + +See [the exact declarations and resource units](G1API.md) and the +[public consumer](../../../Examples/SchemaExamplesConsumer/README.md). + +## Observed qualification + +Native environment: macOS 27.0 arm64, Xcode 27.2 build 27B5019j, Apple Swift 6.4 +`swiftlang-6.4.0.34.1`, selected SDK 27.2, language mode 6. Test binary inspection +confirms minimum macOS 27.0; the Swift Testing host-framework banner is not the +package deployment target. No compatibility flag or unsafe annotation was added. + +| Command/check | Observed result | +| --- | --- | +| `python3 Scripts/ci.py --lane schema-examples` | Exit 0 on implementation commit; all six commands passed | +| New Swift Testing target | 21 functions, zero failures; exact module identity and cardinality enforced | +| Existing JSONSchemaTests | 69 functions, 12 exact parameter invocations, zero failures | +| Pinned schema suite within existing tests | 1,301 core + 125 optional semantic + 874 format cases = 2,300 evaluated, zero failures | +| Schema policy ledger | 2,337 discovered; 37 separate policy cases, unchanged; exclusions are not evaluated successes | +| NFC qualification within existing tests | 18,853 cases / 94,265 comparisons | +| Public consumer | 12 cases; independent native verification and roundtrip of every returned value, including the offline registry case | +| Public compiler boundaries | Two intended typecheck exits 1 for package-only plan access and internal certificate initialization | +| `python3 Scripts/ci.py --lane schema-examples-safety` | Exit 0: 21 Release ASan functions, one shared-generator TSan function, strict memory-safety target/dependency build with warnings as errors | +| `python3 Scripts/ci.py --lane docs` | Exit 0: seven native DocC catalogs; 988 authored declarations documented, including all 42 new declarations and named parameters; README consumer and local static site checks | +| CI event parser checks | 19 existing parser receipts passed | +| CI negative/mutation checks | 12 acceptance/rejection checks; wrong products, back-edges, dependencies, missing functions and a focused-safety substitution rejected | + +Suite pin: `json-schema-org/JSON-Schema-Test-Suite` +`5b0ee1613e45fcc2bddac00e07c19cd49b00d8a8`. Meta-schema pin, Unicode 13 data, +upstream licenses, resource selections and policy tables are unchanged. The tests +run the pinned inputs already in this repository; no online resolution is used. +All 49 baseline finite policies and their full-lane cap probes remain required. +The full root requirements are now 440 Mac functions / 325 parameter invocations +and 16 shared-state functions. Focused G1 runs do not claim that full inventory ran. + +Private materialized records are retained under ignored `Artifacts/SchemaExamplesS3/G1`. +The return bundle contains command logs, events, source hashes, input pins and a +mapping from qualified source to the final archive. The successful implementation +native receipt is `native-committed/receipt.json`; safety is `safety-first/receipt.json`. +The strengthened consumer also runs in the final docs lane. Unchanged code evidence +is reused with file-hash comparison, not asserted from a summary alone. + +## Review findings and retained failures + +The initial full-package test compilation failed with disk-full errors before any +tests ran. Native Swift 6.4's actual `--test-product` option selects the generated +test bundle and preserves exact discovery. An invalid build-option combination +and a skip-build attempt on incomplete unrelated bundles are retained as failed +tool probes, not passes. + +One format-policy fixture initially omitted the metadata vocabulary needed to +offer its default candidate; adding that vocabulary preserved the intended format +rejection test. A real harness ambiguity after sanitizer builds was fixed by using +SwiftPM's reported consumer binary directory instead of recursively selecting from +all caches. Both failed runs and subsequent passing runs are retained. + +Review also corrected discovery accounting to charge schemaNodes, separated live +discarded-result release from cumulative work, and strengthened the offline public +consumer's independent validation/roundtrip and public remaining-work demonstration. +No test count, expected engine verdict, resource baseline or suite policy was lowered. + +## Remaining scope + +Only minimal is exposed; G1 records seed but draws no random numbers. The algorithm +is `s3-g1-candidates-v1`, with the documented `swift-json-schema-snapshot-g1-v1` +transport. The digest normalizes key order and numeric spelling; it does not recover +original order/lexemes or source text. G2 must freeze emitted-byte/corpus/trace +identity and metered random construction. No cross-platform determinism claim is +made from native-only G1 results. + +General constraint-driven synthesis, richer profiles, the 44-case generation +ledger, advanced/dynamic construction, complete G5 adversarial lifecycle proof and +full G7 Native/Linux/WASI/mobile qualification remain open. No controlled performance +or RSS result is claimed. No branch was pushed, PR opened, merge performed, tag +created or release published. Sibling work and the original main checkout were +not changed by this implementation. diff --git a/Documentation/Architecture/Release1.6/G2Determinism.md b/Documentation/Architecture/Release1.6/G2Determinism.md new file mode 100644 index 0000000..3198d54 --- /dev/null +++ b/Documentation/Architecture/Release1.6/G2Determinism.md @@ -0,0 +1,100 @@ +# G2 determinism and accounting decision + +Accepted on the isolated S3 branch before the G2 implementation. This supersedes +G1's provisional algorithm/digest identifiers, preserving its native certification +authority and explicit unsupported cases. General synthesis remains G3/G4 work. + +`s3-candidates-v2` uses `splitmix64-v1` (the packet's wrapping constants and shifts). +Each raw draw costs 10 core work units, including rejected draws. Uniform selection +adds three units for the rejection threshold and two per comparison/modulo. The +threshold is `(0 &- bound) % bound`; zero bounds throw. Quota counters never wrap. +Mutable RNG/trace state is noncopyable and invocation-owned. Swift 6.4 initially +inferred Sendable for these internal value types; explicit unavailable Sendable +conformances now opt them out. Compiler fixtures reject copying and Sendable +requirements for the mutable helpers and accept moves plus the sharable facade. +Fixed draw/threshold/comparison costs are charged one unit at a time, including +inside a draw, so cancellation quantum one remains meaningful. No default Hasher, +locale, clock, network, callback or floating-point conversion affects decisions. + +Candidate roles use ASCII keyword order: const, default, enum, examples; then +true/empty-schema candidates and compiler-qualified static references in ASCII +keyword order ($dynamicRef with a static target, then $ref). Array order +is semantic and retained. Minimal scans arrays from zero without draws; +demonstration picks an unbiased starting offset and scans circularly, without +replacement. It also chooses from the immutable synthetic corpus for true/empty +schemas. This is seeded candidate selection, not general demonstration synthesis. +There is no type preference until typed synthesis is implemented in G3. + +Every proposed value is constructed by the existing iterative strict writer +(compact, scalar UTF-8 key order, normalized exact decimals), followed by the +existing strict parser using the SAME meter. This normalizes object insertion +order and numeric lexemes without changing scalar identity or mathematical value. +Nested arrays remain arrays; no Dictionary, Double or approximate +arithmetic intervenes. Construction/admission/validation work remains cumulative. +Discarded attempts refund only their owned live bytes, excluding retained traces. + +## Byte transports, version 1 + +All digests are lowercase SHA-256. `u64` means eight bytes, unsigned big-endian; +`field(b)` means `u64(byteCount) || b`. Literal headers include their final LF. +Text is exact UTF-8. These are Swift-JSON transports, **not RFC8785/JCS** or hashes +of unavailable original source text. No Unicode normalization is performed. + +* `swift-json-schema-snapshot-v1`: header `SwiftJSON-schema-snapshot-v1\n`, + u64 document count, root followed by registry in caller order. Each document is + one URI-presence byte (0/1), field(retrievalURI UTF-8, empty when absent), + field(compact scalar-key normalized-number strict writer bytes). Source maps, + original numeric lexemes and policies are excluded. Registry order is identity. +* `swift-json-example-corpus-v1`: header `SwiftJSON-example-corpus-v1\n`, + field(the same writer encoding of the ordered corpus as one JSON array). + The built-in corpus is `[null,false,0,"example",[],{}]`. Custom values are + admitted, normalized and hashed during preparation; caller array construction + precedes preparation. Empty corpora are rejected. Corpus order is identity. +* `swift-json-example-policy-v1`: header `SwiftJSON-example-policy-v1\n`, + u64 maximumAttempts, then three limit records in generation, compilation, + validation order. Each record is u64(49), then for every JSONResourceMetric in + declaration order field(metric rawValue UTF-8), u64(limit). Compilation flags + then validation flags are four bytes each: formatAssertion (0/1), output + (flag=0/basic=1), diagnosticPolicy (complete=0/bestEffort=1), collectAnnotations + (0/1). This binds every one of the 49 caps, including cancellation quantum. +* `swift-json-example-trace-v1`: header `SwiftJSON-example-trace-v1\n`, + fields of generatorVersion, source digest, corpus digest, policy digest (ASCII), + u64 seed, profile byte (minimal=0/demonstration=1). Events are one code byte + followed by two u64 operands: visit=1(0,0), offer=2(role,index), draw=3(raw,bound), + verdict=4(attempt,valid:0/1), finish=5(attempts,draws). Role IDs: const=0, + default=1, enum=2, examples=3, true/empty=4. No compiler-local IDs, arbitrary + schema strings, values, diagnostic text, timing or work counts enter the trace. + +Every trace byte charges diagnosticBytes, retainedBytes and work before append; +native diagnostics share that cumulative diagnosticBytes ceiling. There is no +silent truncation or partial-digest success. RNG draw events include rejected +draws. A successful trace includes the final native valid verdict and finish; +hashing and result metadata are charged before returning. Failure/cancellation +returns no certificate or accounting success. Digesting excludes its own hash +and work counts, avoiding circular definitions. Stable trace bytes are distinct +from platform-dependent diagnostic/work observations. + +Preparation shares its compiler meter with corpus admission, all transports and +hashing. Generation uses the intersection of generation/evaluation limits. +Existing iterative codec/admission frames bound depth before growth; no recursive +constructor is introduced. Logical accounting is conservative live ownership, +not allocator/RSS or caller-instruction accounting. G7 must still run these +goldens on Linux/WASI before a cross-platform qualification claim. + +## Accepted API additions and qualification boundary + +`JSONSchemaExampleOptions` adds `corpus:` (default `.synthetic`). The corpus is +`JSONSchemaExampleCorpus(values:)`; its values are immutable and caller-authored +until checked preparation. `.demonstration` is the only added profile; boundary +and typed construction remain deferred. Prepared generators expose corpusDigest +and policyDigest. Results add corpus/policy/trace algorithm IDs and digests, +rngAlgorithm, randomDraws and traceEventCount. Result initialization stays internal. +The existing public JSON and JSONSchema APIs/dependencies are unchanged. + +The focused `schema-examples-determinism` lane checks exact product/dependency +shape, all 42 generator tests in Debug and Release, implementation ownership +compiler probes and the real public consumer plus independent transport oracle. +The broader `schema-examples` lane and full release/safety lanes remain intact. +Unchanged-core suite reuse is tied to actual file path/hash sets in the G2 source +evidence map; it never upgrades a failed aggregate receipt to a passed lane. +No core metric probe or parameter/cardinality guard is removed. diff --git a/Documentation/Architecture/Release1.6/G2Qualification.md b/Documentation/Architecture/Release1.6/G2Qualification.md new file mode 100644 index 0000000..e2fa66c --- /dev/null +++ b/Documentation/Architecture/Release1.6/G2Qualification.md @@ -0,0 +1,90 @@ +# G2 native increment and evidence + +G2 adds metered SplitMix64 choices, stable exact construction and versioned +source/corpus/policy/complete-trace digests to the optional JSONSchemaExamples +product. The accepted API and normative transports are in G2Determinism.md. +G0/G1 history remains intact; G3-G8 remain open and release_ready remains false. +No existing JSON/JSONSchema production source or root dependency was changed. + +## Actual native outcomes + +On macOS 27.0 arm64, Xcode 27.2 (27B5019j), Apple Swift 6.4 +(swiftlang-6.4.0.34.1), selected SDK 27.2, language mode 6: + +* `python3 -B Scripts/ci.py --lane schema-examples-determinism --output Artifacts/SchemaExamplesS3/G2/determinism-verified` + exited 0: seven commands, exact eight-product/empty-dependency checks, + 42 Swift Testing functions in Debug and 42 in Release. The public consumer + passed 15 cases in each of two independent processes; two public access-control + negatives rejected as expected. Independent stdlib SHA-256/byte-format vectors + matched both profiles and all 49 policy fields. +* The implementation compiler qualifier accepted moving RNG state once and + sharing the immutable facade; it rejected double consumption of RNG/trace and + Sendable requirements for RNG/trace/transport (one positive, five negatives). + These trusted @testable fixtures are distinct from the public-only consumer. +* `python3 -B Scripts/ci.py --lane schema-examples-safety --output Artifacts/SchemaExamplesS3/G2/safety-verified` + exited 0: all 42 functions in Release ASan, one TSan function with 32 concurrent + invocations (plus sequential reference calls), and strict-memory-safety with + warnings as errors. Every command exited 0; no sanitizer failure was ignored. +* The broader schema-examples run evaluated all 69 existing schema functions, + 12 parameter invocations and 2300 pinned cases (1301 core, 125 optional semantic, + 874 formats), with zero failures. Its 37 policy exclusions are separate. + That aggregate run failed its stale **12-case consumer marker**, despite the + actual 15-case consumer/oracle passing. Only its separately passed unchanged-core + schema command is reused. The final seven-command G2 lane refreshed all affected + implementation/consumer checks; the failed aggregate is never called passed. +* Harness mutation checks passed 17 cases; Testing event parser checks passed 19. + Exact module/function/parameter discovery and all 49 baseline metric probes + remain required in the full lanes; a focused lane cannot substitute for them. + +Raw commands, exits, environment, input SHA-256 lists, Swift Testing event streams +and log hashes are materialized under the private G2 evidence directory. Source +receipts record G1 HEAD 8de39d5 plus the actual working-tree hashes at execution; +that Git SHA alone does not contain G2. The return's SourceEvidenceMap verifies +complete path/hash sets against the committed final source and separately verifies +unchanged-core suite reuse. The final clean-head `Scripts/ci.py --lane docs` +receipt records actual DocC/comment/consumer/site outcomes, rather than inferring +success from authored prose. Build outputs/private logs remain outside source Git. + +## Meaningful behavior and resolved findings + +The 21 added functions exercise the 16 reference draws, 640 bounded selections, +a forced rejected draw (two draws, 61 work units, 34 trace bytes), terminal quota +failure, quantum-one cancellation inside a draw, normalized exact large/fractional +numbers and scalar-distinct keys, 100 nested arrays, numeric/depth admission, +corpus admission/order/immutability, independent transport/trace goldens, all 49 +caps and all policy flags, exact trace capacity, circular scans including false +verdicts, stable keyword/reference traversal and constructor cancellation. +The strengthened sharing function covers both minimal and seeded demonstration. +Native certification against the original compiled root remains the sole success +path; no validation report constructor or alternate validator was introduced. + +Compiler probes exposed implicit Sendable inference on internal noncopyable value +types. Explicit unavailable conformances now opt mutable helpers out; no unsafe +annotation or compiler compatibility flag was added. A throwing noncopyable member +initializer was replaced with local construction followed by a final move. + +Exploratory failures remain retained: an incorrect fixed-decimal expected byte +string was corrected to the existing writer's normalized coefficient/exponent +spelling; mathematical exactness assertions remain. A corpus test initially hit +the packaged meta-schema string cap first. Its positive control exposed that gap; +the corrected test admits a null corpus at 4096 bytes and rejects a 4097-byte +corpus token. Production/default limits were not changed. The consumer marker +was updated to the actual 15 cases and stale/missing markers now fail mutation +checks. An unavailable Python cache and misspelled parser-probe command were +replaced with cache-free syntax checking and the actual 19-case verifier. + +## Remaining boundaries + +This is a native G2 milestone, not full schema generation, a satisfiability solver +or a release candidate. G3/G4 constructive fixtures, G5 complete lifecycle review, +G6 final documentation and G7 platform qualification remain separate. No new +Linux/WASI/mobile, controlled performance or RSS qualification is asserted. +Trace bytes never silently truncate; failure/cancellation returns no certificate. +Logical retained weights and owned work do not cap allocator RSS or caller code. + +Main independently advanced to 037e735 with 1.5.1 repairs while this isolated train +retained its approved G0/G1 base. That includes a schema best-effort diagnostic +repair and restored tests/harness checks. Integrate and reconcile that source and +its new cardinalities before G7/G8; this G2 result does not qualify that merge. +The separate main/sibling checkouts were not edited. No push, PR, merge, tag, +release, deployment or external message was performed. diff --git a/Documentation/Architecture/Release1.6/G3Construction.md b/Documentation/Architecture/Release1.6/G3Construction.md new file mode 100644 index 0000000..1733549 --- /dev/null +++ b/Documentation/Architecture/Release1.6/G3Construction.md @@ -0,0 +1,61 @@ +# G3 conservative typed construction + +G3 extends the isolated G2 source at 42a523c. Native JSONSchema compilation and +complete root validation remain the only dialect/success authorities. No core +JSON or evaluator semantic changes, online resolution or dependencies are added. + +After declared candidates are tried, a finite typed search constructs candidates +from compiler-qualified nodes. Positive allOf and static reference constraints +are gathered as conjunctions, not merged JSON schemas. AnyOf/oneOf child hints are +separate alternatives and every proposal is validated against the original root. +Dynamic construction and same-instance reference cycles remain explicit failures. +Nested instance construction uses an explicit frame stack; no recursive Swift +constructor or nested helper meter is introduced. + +Type selection uses a fixed order: null, boolean, number (integer when required), +string, array, object. A declared number accepts integral and fractional values; +an integer restriction retains only mathematically integral candidates. Keyword +and member identity uses exact decoded scalar UTF-8. Required keys are the union +of active conjuncts, sorted exactly. Every applicable property/additional schema +contributes constraints for that member; no last-wins dictionary or inferred +requiredness is used. Optional false properties can be omitted. MinProperties +can add declared or synthetic names under each applicable additional schema. +Arrays honor basic sizes and prefix/items declarations. Unique items vary leaf +choices, with uniqueness certified only by the full validator. + +Exact numeric proposals use bounded signed decimal digit arithmetic. A lower +bound is rounded to an exact positive step (one for integers; a decimal step +derived from bound precision for numbers). MultipleOf supplies an exact step; +integer multiples remove only the denominator's factors of two/five. Open bounds +advance by that exact step, never an arbitrary epsilon. Dense alignment beyond +numberDigits fails before allocation. Upper-bound proposals are used for negative +ranges. This is conservative search, not a general numerical constraint solver. + +Strings count Unicode scalars. Minimal uses the shortest available filler; seeded +demonstration can use admitted synthetic tokens. Optional asserted email uses a +fixed example.com token. Format metadata never enables assertions. Arbitrary regex +inversion, dynamic scope, difficult conditionals/dependencies and evaluated-location +construction remain G4 or explicitly incomplete. Defaults/examples remain hints +and can be invalid; no value is repaired or schema rewritten. + +All staging, key comparisons, decimal digits, queue/stack growth, reference steps, +trace bytes and final native checks use one noncopyable invocation meter. Each +candidate has checked value capacities; work and lifetime totals never reset. +Discarded staging refunds only its live logical ownership, retaining trace bytes +and cumulative work. Frame/value/slot weights use JSONLogicalWeights v1; qualified +constraint views reserve 64 logical bytes per slot. Bytes are not RSS. Caller +fixtures and ordinary Swift Testing oracle calls are separate qualification work. + +The candidate algorithm becomes s3-candidates-v3. Snapshot/corpus/policy v1 byte +transports remain unchanged. Trace becomes swift-json-example-trace-v2 with header +SwiftJSON-example-trace-v2 LF, the same envelope/events as G2, plus offer role 5 +(typed proposal), and code 6(depth,variation) for a typed value request. G2 vectors +remain preserved in history/archives; G3 vectors bind the new algorithm/version. +No cross-version or unexecuted cross-platform identity is promised. + +Qualification must execute all 24 G3 must-generate IDs, both existing profiles and +multiple deterministic seeds; separately check two sound trivial proofs. Every +success must also pass an independent public native validation and strict value +roundtrip. The complete amended 44-case fixture is a pinned input, not a claim +that G4's remaining cases are implemented. A machine-readable capability ledger +keeps implemented hints, conservative search and deferred features distinct. diff --git a/Documentation/Architecture/Release1.6/G3Qualification.md b/Documentation/Architecture/Release1.6/G3Qualification.md new file mode 100644 index 0000000..79793c4 --- /dev/null +++ b/Documentation/Architecture/Release1.6/G3Qualification.md @@ -0,0 +1,112 @@ +# G3 native construction milestone + +G3 extends G2 commit `42a523c6d0a4dab9195c847feb4e47b43803bfd1` on the +isolated `codex/1.6-schema-examples` branch. The accepted construction and transport +decisions are in [G3Construction](G3Construction.md); the machine-readable +[capabilities](Capabilities.json) distinguish proposal hints from native semantic +authority. No existing JSON/JSONSchema production source or dependency changed. +G0-G2 history remains intact. G4-G8 and main's independent 1.5.1 integration remain +open; this is not a release candidate. + +## Actual commands and outcomes + +On macOS 27.0 arm64, Xcode 27.2 (27B5019j), Apple Swift 6.4 +(`swiftlang-6.4.0.34.1 clang-2100.3.34.1`), SDK 27.2, language mode 6: + +* `python3 -B Scripts/ci.py --lane schema-examples-construction --output Artifacts/SchemaExamplesS3/G3/native-final` + exited 0, all eight commands passed. Exact eight-product isolation and empty + dependencies passed. All 61 Swift Testing functions passed in Debug and Release. +* `python3 -B Scripts/ci.py --lane schema-examples-safety --output Artifacts/SchemaExamplesS3/G3/safety-final` + exited 0, all three commands passed: 61 Release ASan functions, two TSan sharing + functions with 32 concurrent invocations each, and strict-memory-safety + compilation with warnings as errors. No unsafe annotation or compatibility + flag was introduced. +* The actual public consumer independently compiled plans, certified/validated + and strictly roundtripped all 24 required cases, two profiles and four seeds + (192 invocations per process), in two separate processes. Exact values, traces + and cumulative statistics matched. Each process checked 90 fixture witness + expectations and two separate trivial proofs. These are repeated executions, + not 384 distinct fixtures. The other 18 G4 cases are not counted as successes. +* The preserved default public consumer passed 15 cases twice and two expected + public access-control rejections. The independent byte-format/SHA-256 oracle + matched both profiles. Implementation-only compiler fixtures accepted one move + and immutable-facade sharing, and rejected eight copy/Sendable violations with + the expected ownership diagnostics, not missing imports. +* Harness mutation checks passed 22 cases; the Testing event parser passed 19. + Exact full-lane discovery remains 480 native functions, 325 parameter + invocations, 17 sharing functions and all 49 baseline metrics. Focused G3 + results do not substitute for those later release lanes. + +Commands use `DEVELOPER_DIR=/Applications/Xcode-beta.app/Contents/Developer` and +the selected Xcode's macOS 27.2 SDK. Receipts retain the actual command arrays, +environment, exits, log/event hashes and complete working-tree source hashes. +Their recorded Git HEAD is the G2 base plus those captured G3 files; that SHA +alone does not contain this implementation. The private final SourceEvidenceMap +compares full critical path sets/hashes to the committed source. The final +clean-head `Scripts/ci.py --lane docs` receipt records actual documentation, +compiled-consumer and site results for that final snapshot. + +## Regression evidence and fixture identity + +The amended fixture bytes are pinned to +`5b3e6e40e83439a810d0eb7ac412ce158a4e582f36b9d36d6e313c159654ddd3`. +The unchanged original is separately retained at +`6f2bfd4fdb7c7e206b6570c2156ca8e248831840733b0258aa8cd5938b35649f`. +The [manifest](../../../Tests/Fixtures/SchemaExamples/Manifest.json) preserves +G0's integral-number amendment and the complete 44-case/155-witness inventory. +The G3 subset has 90 witness expectations; no old negative was removed to qualify +the generator. These authored fixtures are not independent external-suite passes. + +A 654-file path-set/hash comparison proves the existing JSON/JSONSchema/regex +source, 69 schema functions, their pinned fixtures and qualification policies +unchanged from G2's separately passed schema command: 12 parameter invocations, +2300 evaluated pinned cases and 37 separate policy exclusions. Its aggregate G2 +lane failed a stale consumer marker and is never relabelled passed. The reuse +record explicitly excludes the new G3 fixture directory, which that old suite +does not read. Raw selected-command logs/events and the original receipt accompany +the G3 return; no fresh whole-package or platform-suite pass is inferred. + +## Behavior and review findings + +The 19 added functions cover the fixture matrix, sound proofs, conjunctive exact +NFC/NFD keys and additionalProperties scope, huge/tiny/open signed decimals, +scalar lengths, native format policy, min-properties/unique arrays, nested +arrays, exact cumulative work boundaries after a false verdict, finite staging +caps, dense-alignment rejection, terminal operational failure/cancellation, +100 repeated calls, concurrent sharing, twelve positional prefix indices, +2940 independent integer rounding checks and repeated static-target DAGs. + +Review found that normalized JSON number spelling cannot represent positional +indices: index 10 needs `"10"`, not `"1e1"`. The constructor now uses prepaid, +bounded positional UInt64 formatting. Repeated static references are deduplicated +as DAG sharing; only an active ancestor cycle is rejected. Internal frame types +explicitly opt out of Sendable inference; compiler fixtures enforce that boundary. + +The first exploratory run's old format-hint test expected search exhaustion. +Typed construction correctly found null: format alone does not imply string. +The test now separately verifies the invalid string against native assertion and +uses one attempt to exercise the rejected hint. Another old invalid-default test +now expects a later independently constructed valid object while proving the +original default remains unchanged. The corrected 60-function exploratory run +and final 61-function lanes passed; failed attempts remain in private evidence. + +## Remaining limits + +The candidate algorithm is `s3-candidates-v3`; complete trace is +`swift-json-example-trace-v2`. Source/corpus/policy v1 transports remain unchanged; +G2 vectors remain historical. No cross-version/platform identity is implied. +Every returned value still requires completed validation of the original root. +No schema merge, branch-local certificate, alternate validator, approximate +number, online resolver or unconstrained retry is introduced. + +Construction is conservative: arbitrary regex inversion, dynamic scope, difficult +dependencies/conditionals and general satisfiability are not solved. Dense decimal +alignment can exhaust a finite quota. All owned staging and every native false +verdict share the generation ledger; preparation has its separately reported +compilation ledger. Scratch retention is conservatively charged. Logical bytes +are not allocator RSS and caller qualification work is not an instruction quota. +G5's broader lifecycle review, G6 final documentation and G7/G8 integration, +Linux/WASI/mobile/performance/release qualification remain separate. + +No sibling source, main checkout, remote branch, PR, tag, release, deployment or +external message was changed by this local milestone. diff --git a/Documentation/Architecture/Release1.6/G4Construction.md b/Documentation/Architecture/Release1.6/G4Construction.md new file mode 100644 index 0000000..733213b --- /dev/null +++ b/Documentation/Architecture/Release1.6/G4Construction.md @@ -0,0 +1,46 @@ +# G4 conservative advanced proposals + +G4 extends G3 commit 3951b99 on the isolated 1.6 branch. The original compiler, +qualified offline plan and complete root validator retain exclusive semantic +authority. No schema evaluator, public API, dependency or platform is added. + +The ten additional must-generate fixtures and eight may-fail fixtures are distinct +acceptance groups. Every successful proposal must pass full native validation and +strict roundtrip. A may-fail fixture may also succeed; unsupported/exhausted is +incomplete search, never a proof of unsatisfiability. Operational errors and +cancellation terminate the invocation and cannot be counted as incomplete search. + +Two narrow extensions are selected: + +* Array length includes each active contains declaration's minimum (default one). + Only when contains applies are minContains/maxContains consulted. The first N + items receive that declaration's qualified contains child in conjunction with + their own prefix/items constraints. Several contains declarations remain separate + conjuncts. The native validator alone counts actual matches, checks maxima and + propagates evaluated-location annotations. Avoiding extra matches or finding + disjoint matching partitions remains incomplete; no independent matcher runs. +* A private proposal-only scanner handles fully anchored ASCII sequences: printable + literals, escaped regex punctuation, a single printable ASCII range per character + class, and exact {n} repetition. Variation chooses within each range. It does + not match strings, invert arbitrary regex, accept malformed schemas or replace + native regex. Unsupported syntax falls back to the existing finite token/filler + search and still requires native certification. Output bounds are checked before + append; source scans, repeat counts, output copies and rejection paths share the + invocation ledger. No Foundation regex or private runtime dependency is added. + +Existing finite alternatives handle the conditional fixture without interpreting +if/then/else: the rejected adult/age-zero object consumes work, then a later child +proposal can pass. Optional dependent/pattern/name declarations may be satisfied +by an empty object; that demonstrates fixture coverage, not general constructive +support. Unevaluated-location semantics are certified by native annotations, not +reimplemented. Static references use compiler-qualified identities, including +explicit external registry resources and anchor/base transitions. Dynamic targets +are not guessed; reaching a dynamic-scope edge remains unsupported. Omitting an +optional recursive/dynamic child may produce a valid finite witness. + +Algorithm becomes s3-candidates-v4. The trace v2 grammar is unchanged: existing +typed-request/offer events suffice, and the trace envelope binds the new algorithm. +Snapshot/corpus/policy v1 and SplitMix64 v1 remain unchanged. G3 vectors and archives +remain immutable historical evidence; no cross-version/platform identity is claimed. +All new helpers reuse the one noncopyable generation meter. G5's broader lifecycle +review and G6-G8 integration/platform/release gates remain separate. diff --git a/Documentation/Architecture/Release1.6/G4Qualification.md b/Documentation/Architecture/Release1.6/G4Qualification.md new file mode 100644 index 0000000..1821f41 --- /dev/null +++ b/Documentation/Architecture/Release1.6/G4Qualification.md @@ -0,0 +1,100 @@ +# G4 native qualification + +G4 extends G3 commit 3951b99bb0f690098cf8bef61ce872119e4835da on the +isolated `codex/1.6-schema-examples` branch. See the accepted +[G4 proposal boundary](G4Construction.md), [capabilities](Capabilities.json) and +[progress](Progress.json). G0-G3 history is preserved. G5-G8 and main's independent +repair integration remain open; this is not a release candidate. + +## Actual outcomes + +On macOS 27.0 arm64, Xcode 27.2 (27B5019j), Apple Swift 6.4 +(swiftlang-6.4.0.34.1 clang-2100.3.34.1), selected SDK 27.2, language mode 6: + +* `python3 -B Scripts/ci.py --lane schema-examples-advanced --output Artifacts/SchemaExamplesS3/G4/native-final` + exited 0; all nine commands passed. Exact eight-product isolation and empty + dependencies passed, with 79 Swift Testing functions each in Debug and Release. +* `python3 -B Scripts/ci.py --lane schema-examples-safety --output Artifacts/SchemaExamplesS3/G4/safety-final` + exited 0; all three commands passed: 79 Release ASan functions, three TSan + sharing functions with 32 concurrent invocations each, and strict-memory-safety + compilation with warnings as errors. No unsafe conformance or compatibility + flag was added. +* The actual public G4 consumer passed all ten additional required cases across + two profiles/four seeds: 80 mandatory successes per process. It also exercised + all eight may-fail cases: 32 certified calls from four cases, and 32 explicit + incomplete-search calls from four cases, totaling 144 calls per process. + Two separate processes matched exact values, outcomes, digests and successful + accounting. Every success passed independent public native validation and a + strict roundtrip. No value or successful work report was invented after error. +* All 44 schemas/155 fixture witness expectations received native verdict checks, + including six negatives attached to the G3 trivial-proof cases. The preserved + G3 consumer additionally passed 192 required combinations and two proofs in + each process. The default consumer passed 15 cases twice and two public compiler + negatives. Independent stdlib byte-format/SHA-256 vectors matched both profiles. + Trusted implementation compiler probes accepted one positive and rejected eight + copy/Sendable violations for the expected diagnostics, not missing imports. +* Harness acceptance/rejection mutations passed 26 cases; Testing event parser + regressions passed 19. The capability ledger classifies all 59 compiled keyword + cases exactly once, with referenced behavior functions verified to exist. + Full release lanes still require 498 functions, 325 parameter invocations, + 18 sharing functions and all 49 baseline metric probes. Focused runs do not + substitute for those later full release gates. + +Commands explicitly selected Xcode and its SDK through DEVELOPER_DIR/SDKROOT; +there was no global toolchain switch. Native/safety receipts record G3 HEAD plus +captured G4 working-tree hashes. The final SourceEvidenceMap verifies complete +critical file sets/hashes against committed source. Only maintainer architecture/ +progress prose can differ. The final clean-head docs receipt records actual DocC, +consumer and static-site outcomes for the final snapshot. Private receipts retain +all actual command arrays, exits, toolchain, source inputs and log/event hashes. +No controlled-performance or RSS measurement is claimed on this shared host. + +## Honest outcomes and semantic authority + +The may-fail cases oneof-overlap, recursive-tree, dynamic-ref and +external-offline-ref produced native-certified witnesses. The dynamic case omits +an optional child; constructive dynamic navigation is not qualified. The +conflicting-allof and not-string cases returned candidateConstruction unsupported; +unfulfillable-unique-enum and oneof-overlap-limited exhausted 16 attempts. The latter +has valid fixture witnesses: exhaustion is explicitly not an unsatisfiability +proof. No operational error/cancellation can count as accepted incompleteness. + +The new contains hints choose minimum matching positions beside each conjunct's +prefix/items constraints. Native validation alone establishes all matches/maxima +and evaluated locations. The anchored ASCII scanner proposes printable literals, +escaped punctuation, single range classes and exact repeats. It never matches +strings or replaces native regex. General inversion, disjoint contains partitions, +triggered dependency/key synthesis, dynamic scope and satisfiability remain incomplete. +Optional omission satisfying a fixture does not imply broader constructive support. + +The 18 added tests cover the full G4 fixture family, ten unsupported grammar forms, +Unicode corpus identity, pattern conjunctions, contains defaults/zero/inactive +siblings, prefix/unique/unevaluated composition, surplus-match failure, cardinality +and scanner work caps, malformed/native regex failures, cancellation latching, +conditional false-verdict work, qualified external anchors/base transitions, +missing resources, reached dynamic scope, recursive depth bounds and concurrency. +The conditional test proves the exact sum of rejected and accepted native evaluation +work, success at that cumulative ceiling and failure one unit below it. +All helpers reuse the one generation ledger; no nested meter or work reset is added. + +Algorithm becomes s3-candidates-v4; trace grammar stays swift-json-example-trace-v2 +and binds the new algorithm. Source/corpus/policy v1 and SplitMix64 v1 are unchanged. +Historical G3 vectors/archives are retained. The original/amended fixture bytes +and all classifications are unchanged; the manifest adds explicit G4 ID groups. +The amended SHA-256 remains +5b3e6e40e83439a810d0eb7ac412ce158a4e582f36b9d36d6e313c159654ddd3. + +## Regression and remaining gates + +A 654-file equality proof supports reuse of G2's separately passed schema command: +69 functions, 12 parameter invocations, 2300 pinned evaluated cases and 37 separate +policy exclusions. That old aggregate failed a stale consumer marker and is never +relabeled passed. New S3 fixtures are separately qualified and are not read by +those existing tests. Raw selected-command receipt/log/event bytes accompany the +G4 return. JSON/JSONSchema/regex production source and Package.swift are unchanged. +No fresh external-oracle, Linux/WASI/mobile, full-package or performance pass is +inferred. Logical retention is not RSS; caller work is not an instruction quota. + +Main's independent 1.5.1 repair source and discovery counts must be reconciled +before final release qualification. No sibling source, remote branch, PR, merge, +tag, release, deployment or external message was changed in this milestone. diff --git a/Documentation/Architecture/Release1.6/G5Qualification.md b/Documentation/Architecture/Release1.6/G5Qualification.md new file mode 100644 index 0000000..b505489 --- /dev/null +++ b/Documentation/Architecture/Release1.6/G5Qualification.md @@ -0,0 +1,78 @@ +# G5 native qualification + +G5 extends G4 commit 304f900489dc6c24bce862eccf6adc9865df1027 on the isolated +`codex/1.6-schema-examples` branch. G0-G4 history/archives are preserved. See +[G5 resources and lifecycle](G5Resources.md) and [progress](Progress.json). +G6-G8 and main's independent repair integration remain open. + +## Actual native outcomes + +The supported host was macOS 27.0 arm64, Xcode 27.2 (27B5019j), Apple Swift 6.4 +(swiftlang-6.4.0.34.1 clang-2100.3.34.1), SDK 27.2, language mode 6. +Every command explicitly selected DEVELOPER_DIR/SDKROOT; no global selection changed. + +* `python3 -B Scripts/ci.py --lane schema-examples-advanced --output Artifacts/SchemaExamplesS3/G5/native-qualified` + exited 0, all nine commands passed. Exact manifest/product isolation and empty + dependencies passed. All 96 generator Swift Testing functions passed in Debug + and Release, including 17 added G5 functions. +* `python3 -B Scripts/ci.py --lane schema-examples-safety --output Artifacts/SchemaExamplesS3/G5/safety-qualified` + exited 0, all three commands passed: 96 Release ASan functions, four TSan sharing + functions (32 invocations each, including mixed success/cancellation), and + strict-memory-safety compilation with warnings as errors. +* Public consumers again passed 15 cases/two processes, two access-control compiler + negatives, one trusted ownership positive/eight compiler negatives, and independent + byte-transport/SHA vectors. G3 again passed 192 required calls plus two proofs per + process; G4 passed 144 calls per process (112 certified, 32 explicit incomplete). + All 44 schemas/155 witness expectations received native checks. Two processes + matched outcomes, exact values, digests and successful accounting. Incomplete + search is not a validity verdict or an unsatisfiability proof. +* Harness mutations passed 26 acceptance/rejection cases and the event parser passed + 19 regressions. Exact full-lane requirements become 515 functions, 325 parameter + invocations and 19 sharing functions; all 49 baseline metric probes remain. + These focused runs do not claim execution of later full release/platform gates. + +The final clean-head docs receipt records seven native DocC catalogs, authored API +coverage, README examples, actual public consumer and static-site validation for the +committed snapshot. SourceEvidenceMap verifies full critical file-set/hash equality +for native/safety and exact clean-head equality for DocC. Private materialized +receipts retain commands, exits, toolchain, source inputs, logs and event hashes. +No controlled timing, throughput or RSS result is claimed on this shared host. + +## Review findings and boundaries + +Removed a real unmatched 32-byte refund from trace finalization. A new direct +reservation test proves the transport leaves precisely its 64-byte SHA hex result; +a separate policy transport test proves its real temporary array slots are released. +Candidate values/order, algorithm v4 and trace v2 bytes are unchanged. G4 comparison +receipts verify exact outcomes, traces and work; logical peaks may increase by 32. +No cap, logical weight, public signature, dependency, core/schema or regex source +was changed. Some staging reservations deliberately remain conservative until the +proposal or invocation ends; no exact heap/liveness or RSS guarantee is asserted. + +The actual invocation boundary now latches every terminal error and releases its +remaining logical reservations on success/transfer and failure. Tests preserve a +caller's earlier reservation, repeatedly reuse a borrowed successful ledger without +resetting work, use results after source/registry scope exit, prove false-verdict +work/diagnostics and exact/one-short quotas for seven metrics, and check zero work, +preparation quota, overflow, nested cycles and exhausted branches. Cancellation is +triggered at charged checkpoints in discovery/construction/native regex, long decimal +division and final trace hashing; cleanup cannot clear terminal health or publish a +partial certificate. The prepared immutable generator remains independently reusable. + +Exploratory failures are preserved separately: an initial missing try in a new +assertion, a comparison using a different number representation, and a TSan compiler +rejection of a loop task capture. Final tests use identical parsed exact candidates +and explicit checked Sendable value captures. Diff review restored a legitimate +policy-buffer release accidentally removed alongside the trace refund, and added its +own regression case. The superseded native Release job was deliberately stopped; +its partial receipt is never relabeled a pass. No failure was skipped or silenced. + +A complete unchanged 654-file scope reuses G2's separately passed schema command: +69 functions, 12 parameter invocations, 2300 pinned evaluated cases and 37 separately +classified policy exclusions. The old aggregate failed a stale consumer marker; +that status is preserved. Reused logs/events/source are materialized in the return. +All original/amended fixture bytes and classifications remain unchanged. + +G6 final public documentation, G7 full/platform qualification and G8 release review +remain open. Reconcile main's 1.5.1 repairs and counts before those release gates. +No sibling source, remote branch, PR, merge, tag, release or deployment was changed. diff --git a/Documentation/Architecture/Release1.6/G5Resources.md b/Documentation/Architecture/Release1.6/G5Resources.md new file mode 100644 index 0000000..0b1b49a --- /dev/null +++ b/Documentation/Architecture/Release1.6/G5Resources.md @@ -0,0 +1,82 @@ +# G5 cumulative accounting and lifecycle + +G5 retains `s3-candidates-v4`, SplitMix64 v1 and all existing byte transports. +Candidate order, values, trace bytes and semantic authority are unchanged. The +accounting correction removes an unmatched 32-byte refund during trace hashing. +Successful logical retention may therefore increase by 32 bytes relative to G4; +a previously marginal retention cap may now fail correctly. Historical receipts +and goldens remain available. This changes neither the 49 default caps nor the +version 1 logical weights. + +## Invocation boundary + +Public `generate(seed:profile:)` creates one noncopyable JSONWorkMeter with the +intersection of generation and validation limits. Its internal borrowed-meter +entry is also used by behavior tests. That entry requires the identical policy +and core domain; it is not a public accumulator API. Discovery, reference and +branch expansion, typed staging, exact writer/parser materialization, every native +validation and trace finalization borrow that same ledger. There is no nested +meter and no replacement of a failed ledger. Preparation separately shares one +compiler-domain ledger across compilation and source/corpus/policy hashing. +Caller construction of documents, registries and corpora precedes these operations. + +Completed false verdicts consume evaluation/numeric/regex work, diagnostic bytes +and issue counts. Only their discarded live ownership is released. No failure +report is fabricated from a throwing validator. Operational errors, cancellation, +search exhaustion, unsupported construction and trivial proofs leave the search +terminal. The boundary latches even errors that originate outside the meter +(such as nonproductive native reference evaluation). Catching them in trusted +internal code cannot resume that invocation. A new public call has independent +health; the immutable prepared generator remains reusable and Sendable. + +Work, numeric work, evaluation work, aggregate regex work, issue counts and +trace/diagnostic charges are cumulative. Per-value input/output/node/text ceilings +are retired between materializations; retirement preserves lifetime report totals +and never refunds work. Per-search regex work resets only its per-search ceiling; +aggregate regex work remains cumulative over all candidates. Active depth, frame, +container cardinality and individual token capacities keep their existing meanings. +They are not cumulative generated-node or total-depth counters. There is no new +unlimited setting and no arbitrary-schema completeness claim. + +## Ownership and reports + +Search uses a conservative invocation arena: some construction scratch reservations +are held until the containing proposal/invocation ends even when Swift can release +an individual temporary earlier. Rejected proposal and verdict ownership is +released at their existing boundaries. Complete trace bytes remain charged until +hashing; SHA-256 scratch and the 64-byte digest overlap are checked. No trace +truncation or retained error-history array is introduced. + +A successful report snapshots counts at certification, including the returned +value/verdict, receipt metadata and any still-reserved invocation staging. Before +the borrowed entry returns, it releases its remaining logical reservations as the +result transfers to the caller. On failure it releases all invocation reservations +without changing charged work, diagnostics, retired admission or terminal health. +The caller's preexisting live reservations are preserved. The report's retained +count is a conservative certification snapshot, not a post-return heap measurement. +Peak logical bytes are neither allocated bytes nor process RSS. Callback execution +in the package-only checkpoint test observer is trusted test work, not an owned +instruction quota or a public generator callback. + +No result retains a generator, plan handle, mutable trace or search stack. Its +JSON value, native verdict and policy/identity values remain usable after the +preparation/source/registry scope ends. Immutable preparation retains its admitted +source/registry/corpus/plan until the caller releases it. There is no global mutable +cache. Native sanitizers and concurrent failure/success tests qualify this code; +there is no allocator-independent proof of immediate physical memory reclamation. + +## Qualification boundary + +`LifecycleAndAccountingTests.swift` adds real-engine cases for exact successful +quota/one-short boundaries (seven work/diagnostic/retention metrics), trace ownership, +repeated borrowed calls, source-scope result lifetime, multiple false verdicts, +zero work, exact preparation quota, cyclic construction, exhausted branches, +native evaluation errors, checked overflow and deterministic cancellation. +Cancellation uses actual charged checkpoints rather than sleeps: decimal division, +discovery/construction/native regex and final trace hashing. Every interrupted +search checks terminal latching and reservation cleanup. Separate concurrent +invocations mix cancellation and success without poisoning preparation. + +The focused native/safety/docs receipts qualify this increment only. Full release +G7/G8, main repair integration, Linux/WASI/mobile and controlled performance remain +separate. See G5Qualification.md for observed commands/counts, not inferred passes. diff --git a/Documentation/Architecture/Release1.6/G6Documentation.md b/Documentation/Architecture/Release1.6/G6Documentation.md new file mode 100644 index 0000000..9923b3d --- /dev/null +++ b/Documentation/Architecture/Release1.6/G6Documentation.md @@ -0,0 +1,65 @@ +# G6 public documentation and example contract + +G6 extends G5 commit 30cf344c674a7514db2a578397ade642c0ab942e on the isolated +S3 branch. [Current API](API.md) supersedes provisional G1 interface prose while +preserving G0-G5 history. Runtime behavior, defaults, all 49 caps, eight root +products, private targets, compiler baseline and empty dependencies are unchanged. +The three production Swift files touched in G6 change documentation comments only. + +## Executable examples + +Every Swift block in the five JSONSchemaExamples DocC guides is extracted in +stable filename/block order into the separate public DocumentationExamples.swift. +The generated function runs each block in its own scope with public JSON, +JSONSchema and JSONSchemaExamples imports. Source-location directives point compiler +errors at the authored guide. Synchronization is exact; omissions, additions, +empty blocks, foreign imports and changed bodies fail the checked ten-block contract. + +| Guide | Blocks | Observable assertions | +| --- | ---: | --- | +| VerifiedCandidates | 3 | Exact large integer/native verdict and work; offline definition selection and independent validation/roundtrip; honest exhausted search | +| TypedConstruction | 2 | Exact lower bound; scalar-distinct required keys, present null and omitted optional default | +| AdvancedConstruction | 1 | Contains/items/pattern/uniqueness candidate receives complete native root verdict | +| DeterministicConstruction | 2 | Custom admitted corpus/profile identity; same prepared inputs/seed preserve exact value, trace and work | +| PoliciesAndLimits | 2 | Invalid enum candidate charges work/attempts and checked remaining total; format annotation/default metadata remain unchanged while explicit assertion rejects | + +The existing public qualifier executes this mode in two processes, in addition +to the preserved 15 cases, two compiler negatives, separate process vectors and +independent transport oracle. It checks exact completion markers, never a blanket +nonzero count. Eleven harness mutations exercise source and receipt acceptance/ +rejection and verify all 49 metric classifications. These are harness checks; +Swift behavior remains covered by native Swift Testing and real public consumers. + +The root README adds one exact native-certified schema example, synchronized with +the general Consumer's fourteen executable fences. That consumer explicitly adds +the optional seventh library; no JSON or JSONSchema target gains a dependency. +The separate schema consumer remains JSON/JSONSchema/JSONSchemaExamples only. + +## Scope and accuracy + +Guides, inline comments, README, documentation index, tooling and current API/limits +contracts describe the actual v4 constructor. They distinguish conservative hints +from validator support, explicit incomplete search from proofs and operational +errors, separate preparation from cumulative generation, and explain certification +retention versus heap/RSS. They do not imply a released 1.6 product, cross-platform +release qualification or arbitrary-schema completeness. Historical G1-G5 records +retain their original observations and claims. + +All five guide routes, including PoliciesAndLimits, are required in the native +DocC static-site build. DocC warnings/symbol-link errors remain failures. All 60 +S3 public declarations retain authored comments and named-parameter coverage; +compiler-derived API identity is compared with G5 before closure. + +JSONSchemaDSL is absent from this branch's inspected Package.swift and its main +base. The independent train is untouched; no shim or speculative dependency is +added to demonstrate nonexistent integration. The conditional DSL/manual equality +example is deferred until an available integrated product is reconciled for G7/G8. +No format assertion, compiled-plan behavior or registry default is changed. + +## Qualification and next gate + +Run affected native and documentation lanes through Scripts/ci.py. Preserve exact +96-generator/515-root function counts, 325 parameter invocations, 19 sharing functions, +all 49 metric probes and independent fixture policies. Evidence records commands, +exits, source hashes and selected native tools. G7/G8 full/platform/release gates +and main's independent repairs remain separate; no remote action is implied. diff --git a/Documentation/Architecture/Release1.6/G6Qualification.md b/Documentation/Architecture/Release1.6/G6Qualification.md new file mode 100644 index 0000000..d4c7569 --- /dev/null +++ b/Documentation/Architecture/Release1.6/G6Qualification.md @@ -0,0 +1,78 @@ +# G6 native documentation qualification + +G6 extends G5 commit 30cf344c674a7514db2a578397ade642c0ab942e on the isolated +`codex/1.6-schema-examples` branch. G0-G5 history/archives remain intact. The +[current API](API.md), [all 49 metric scopes](SchemaExamplesLimits.md) and +[documentation contract](G6Documentation.md) govern the current descriptions. +This closes the native G6 increment, not G7/G8 or a 1.6 release candidate. + +## Native outcomes + +macOS 27.0 arm64, Xcode 27.2 (27B5019j), Apple Swift 6.4 +(swiftlang-6.4.0.34.1 clang-2100.3.34.1), SDK 27.2, language mode 6. +Commands explicitly select DEVELOPER_DIR/SDKROOT; no global toolchain changes. + +* `python3 -B Scripts/ci.py --lane schema-examples-advanced --output Artifacts/SchemaExamplesS3/G6/native-final` + exited 0; all nine commands passed, including 96 Debug and 96 Release generator + Swift Testing functions, exact eight-product/empty-dependency checks, one ownership + positive/eight negatives and preserved public/fixture consumers. +* `python3 -B Scripts/ci.py --lane schema-examples-safety --output Artifacts/SchemaExamplesS3/G6/safety-final` + exited 0; all three commands passed: 96 Release ASan functions, four TSan sharing + functions, strict-memory-safety compilation with warnings as errors. +* Every Swift block in all five S3 guides is synchronized with its separate public + consumer: ten snippets execute in two processes, retaining all 15 original cases, + two public compiler negatives and independent process/transport vectors. The new + README example compiles in the general seven-library consumer with fourteen fences. +* G3 repeated 192 required certifications/two proofs per process; G4 repeated 144 + calls per process (112 certified, 32 explicit incomplete). All 44 schemas/155 native + witness expectations were checked. Policy exclusions, incomplete search and + operational errors are not successes. No failure value/report was fabricated. +* Documentation harness passed eleven mutations and exact 49-metric classification; + CI harness passed 26 mutations, and the event parser passed 19 regressions. Exact + 96-generator/515-root/325-parameter/19-sharing and all 49 cap requirements remain. + +The clean-head docs receipt records actual native symbol graphs, all seven DocC +catalogs with warnings as errors, authored declaration/parameter coverage, compiled +README and public guide consumers, and static-site routes for all five S3 guides. +SourceEvidenceMap binds native/safety critical hashes and exact committed DocC inputs. +Private materialized receipts retain commands, exits, source hashes and log/events. + +## API, defaults and source compatibility + +All 181 production Swift files match G5 after removing only documentation-comment +lines; only Corpus, Generator and Options comment files differ in exact bytes. Root +Package.swift and every existing test/fixture match G5 byte-for-byte. The native +public compiler graph comparison verifies all 60 authored S3 declarations/signatures +unchanged. The original compiled plan, registry, format/default behavior and v4 +algorithm/transport IDs remain authoritative. No product/runtime dependency is added; +only the independent general consumer opts into the existing examples product. + +Guides execute exact numbers/scalar keys, optional/default/null distinctions, +offline definition selection, original-root validation and roundtrip, conservative +contains/pattern proposals, seeded replay, remaining work, policy preservation and +honest exhaustion. All new descriptions distinguish constructor hints from native +validator support and logical retention from heap/RSS/caller instruction accounting. +The README clearly labels the API as unreleased 1.6 development. + +JSONSchemaDSL is absent from the inspected S3 manifest and main base. The conditional +DSL/manual equality example is deferred until its product is available during later +integration. No shim, speculative import or sibling edit was introduced. + +## Preserved evidence and remaining work + +An unchanged 654-file core/schema/fixture/policy scope supports reuse of G2's separately +passed 69-function/12-parameter/2300-evaluated-case schema command, with 37 separate +policy exclusions. Its old aggregate failed a stale consumer marker; that status is +preserved. Raw reused command/log/event/source bytes accompany the review return. +No fresh independent oracle, full-package, Linux/WASI/mobile or performance pass is +inferred. G7/G8 and main's independent 1.5.1 repair integration remain open. + +An exploratory edit helper had a Python syntax error before applying its patch; the +corrected structured patch was then qualified. An initial harness parser counted a +Markdown divider as a metric and failed its exact assertion; header handling was +fixed while retaining all 49 checks. Raw exploratory outcomes remain separate from +final passed receipts. No native failure was skipped or ignored. + +Private evidence/caches stay outside Git and source archives. G5 and earlier archives +remain verified unchanged. No sibling source, remote branch, PR, merge, tag, release, +deployment, publication or external message was changed in this increment. diff --git a/Documentation/Architecture/Release1.6/G7Qualification.md b/Documentation/Architecture/Release1.6/G7Qualification.md new file mode 100644 index 0000000..3d7cc48 --- /dev/null +++ b/Documentation/Architecture/Release1.6/G7Qualification.md @@ -0,0 +1,126 @@ +# G7 production reconciliation and qualification + +The isolated `codex/1.6-schema-examples` branch integrates main repair commit +037e735dc6224789e4d04d2c18d15b8d27440198 through local merge +b5bc082f930cad669364de61da4e4191e7bb9cc3. G6 remains at +4368947c0db3831da988b9d8626055a38b42a7e3. The new bounded grammar tests are +cc9ce4f49ca11bbb09273c0102c138c367cba2bc; the consumer scratch-path correction is +6652f9bd65b6f21e1a0c8a22fde5d0ce03b68813. Earlier gates and archives are preserved. + +## Source and behavior + +Main's best-effort diagnostic omission repair preserves an invalid verdict without +poisoning a cumulative ledger; owned work, annotation limits, overflow and cancellation +still fail operationally. Its native regression cases are retained. All 126 restored +maintenance function identities, structured HTML previews, exact policy counts and +49 core probes remain required. The deterministic CLI identity is regenerated from +this package's actual Sources and manifest. + +G7 adds two meaningful Swift Testing functions and a public generated-schema replay: +64 bounded grammar schemas, 512 seed/profile certifications and 64 known-invalid +mutations, plus scalar-key/schema-member permutation checks. A separate seeded +research grammar produces another 64 schemas/512 outputs/64 invalid mutations. +Neither is an exhaustive satisfiability or arbitrary-schema generation claim. + +The complete discovery contract requires 644 native functions, 628 Linux functions, +540 WASI functions, 325 native/Linux parameter invocations and 288 WASI invocations. +Native shared-state safety requires 45 functions. S3 itself has 98 functions and four +shared-generator functions. Exact module identities and every earlier parameterized +function count remain checked; policy exclusions never count as evaluated successes. + +The full native harness now builds the S3 consumer with the explicit root `.build` +scratch path used by its later ownership/generation/matrix helpers. An inspected +SwiftPM default would otherwise place it under the example package. The fix changes +one command argument pair and no runtime, test, cap or conformance policy. + +## Observed qualification + +The complete default native lane passed all 37 commands from an actual clean, +Git-free source extraction. Debug and Release each passed all 644 functions and +325 parameter invocations. The full pinned qualification executable passed with +its unchanged 900-second watchdog. All command exits, raw logs and structured +event streams are retained; source hashes were unchanged throughout. + + +Native host: macOS 27.0 arm64; Xcode 27.2 (27B5019j); Apple Swift 6.4 +(swiftlang-6.4.0.34.1 clang-2100.3.34.1); SDK 27.2; language mode 6. +Every native command explicitly selects DEVELOPER_DIR and SDKROOT. + +- Focused native lane: all ten commands passed, including 98 Debug and 98 Release + S3 functions, one ownership positive/eight negatives, 15 original public cases/two + public-access negatives, ten compiled guide snippets in two processes, G3/G4 + replay and the 512-record matrix in two processes. +- Full safety: 644 Release ASan functions/325 parameter invocations, 45 TSan functions + and strict-memory-safety compilation with warnings as errors passed. +- Full WASI: all 77 checks passed using Swift.org 6.4.0, the matching WASI SDK, Node + and WasmKit. Node ran all 540 functions/288 parameter invocations in Debug and + Release; existing public consumer/49-cap/rooted-vector/corpus/strict-memory checks + retained their explicit host scopes. This does not claim a WASI CLI runtime. +- Public S3 platform consumer: both mobile SDK builds; physical iPhone 14 Pro Max + on iOS 27.2; iPhone/iPad iOS 27.0 simulators; Node and WasmKit all passed 15 public + cases, ten guide snippets and 512 certifications/64 invalid mutations. All 512 + normalized records, including exact values, traces, attempts and work, match byte + for byte across those five executions and native macOS (six platforms total). + +The research oracle remains Python-only tooling. The original packet script reran +44 schemas/154 witnesses. An exact numeric binding reproduces the original known +integral-number expectation error. The preserved G0 amendment evaluates 155 witnesses +with zero disagreements and eight numeric-binding regressions. The generated-value +oracle agrees with all 1,328 checked output records and 128 negative mutations; +32 explicit incomplete-search rows are separate and are not evaluated successes. + +## Failures and scope + +The first native aggregate rejected a SwiftPM lock-wait preamble before manifest JSON. +A later default root Debug command reached its unchanged 1800-second watchdog on the +busy shared host. A cold pilot completed its Debug command in 1277 seconds but one +Apple PDF case threw timedOut. That unchanged case passed in isolation in 3.05 seconds, +and all 97 HTML functions passed in a subsequent bundle run. Original failed receipts +remain failed and separate from later full default-lane outcomes. + +Linux ARM64 preflight/build ran on actual Ubuntu 24.04/glibc with pinned Swift 6.4, +but the guest was interrupted to protect shared disk space. Partial console output +is not a full Linux native/safety pass. The exact pinned compressed input and partial +receipts/build cache are retained for resumption. No native x86_64 host or hosted +qualification was executed for this source. Physical iPad remains deferred under the +existing accepted policy; its SDK/simulator observations do not imply device results. + +All logical retention weights remain distinct from heap/RSS. Charged owned work is +not a caller instruction or wall-clock guarantee. No controlled performance result, +new optimization, fallback engine, public API/default, platform expansion, remote +push/PR/merge, tag, publication or deployment is claimed by this increment. + +## Gate disposition and replay + +G7 is qualified for the observed Apple and WASI scopes and the independent oracle; +its full Linux native/safety portion remains blocked by local storage. The Linux +container command ended with exit 137 after the task-owned guest was stopped; +seven completed commands, including the Release build, had exit zero. The stale +partial receipt still says running and is preserved as interrupted evidence, not +rewritten into a pass. No complete 628-function Linux result or Linux safety result +exists for this source. The task guest was deleted and its temporary runtime was +stopped/unregistered. Pinned compressed inputs, source and build cache are retained. +An x86_64 Linux host is unavailable locally. Physical iPad remains deferred under +the accepted simulator policy. G8 review remains open and release_ready is false. + +The final report materializes the clean source archive, full native/safety/DocC +receipts, independent oracle, platform logs and source mappings outside public Git. +Metadata-only successor changes do not alter the qualified runtime/tests/fixtures. +The source/evidence manifest distinguishes those snapshots; it does not present a +metadata mapping as another full native execution. Final DocC outcomes are recorded +in the report after execution; no remote deployment is implied. + +Use the repository lanes with the supported native environment selected: + +```sh +python3 -B Scripts/ci.py --lane native +python3 -B Scripts/ci.py --lane safety +python3 -B Scripts/ci.py --lane docs +python3 -B Scripts/ci.py --lane schema-examples-qualification +python3 -B Scripts/ci.py --lane wasm +``` + +On pinned Swift 6.4 Linux, resume both linux and linux-safety after sufficient local +space is available, or use separately authorized hosted execution. Preserve 628 +root functions, 325 parameter invocations, 44 shared functions, all 49 caps and the +exact independent-suite policy split. A filtered or WASI pass does not close Linux. diff --git a/Documentation/Architecture/Release1.6/G8CorpusLifetime.md b/Documentation/Architecture/Release1.6/G8CorpusLifetime.md new file mode 100644 index 0000000..ad445a7 --- /dev/null +++ b/Documentation/Architecture/Release1.6/G8CorpusLifetime.md @@ -0,0 +1,38 @@ +# G8 synthetic corpus lifetime repair + +The first hosted S3 qualification at c629114 passed all 98 generator behavior +functions on both Linux architectures, but the complete ASan command exited 1. +Linux LeakSanitizer reported 129 bytes in four allocations from the stored +`JSONSchemaExampleCorpus.synthetic` initializer. Those failed receipts remain +failed. See [the initial Linux run](https://github.com/ThorFuchs/Swift-JSON/actions/runs/38822602731). + +The fixed corpus is now a read-only computed static property. Each access constructs +the same six ordered values for the caller's lifetime: null, false, zero, "example", +empty array and empty object. The original global cache is removed. This is a source +compatible getter for callers; the unreleased declaration changes from stored `let` +to computed `var`. No setter, approximate bridge or extra product is introduced. + +Caller corpus/options construction still precedes checked preparation. Preparation +admits and hashes every value under its existing cumulative ledger. This repair +changes neither corpus contents/order, transport versions, seed/profile choices nor +resource policies. It does not promise an allocation count, process RSS bound or +controlled performance result. The compiler/runtime report does not by itself prove +an invocation-owned reference cycle; the repair removes process-lifetime JSON storage. + +The existing exact-corpus Swift Testing case now exercises repeated reads, exact +encoding and independent caller mutation. Its original digest/order assertions and +all other functions remain required. No test count, cap, timeout or sanitizer check +is reduced. A separate generated public consumer performs 128 scoped corpus/options +reads with exact encoding/copy assertions under Release ASan. Linux explicitly enables +leak detection with no leak suppressions. It must exit zero and emit the exact marker. + +`Scripts/ci.py --lane safety` appends this process check after the existing full +ASan, TSan and strict-memory commands. `linux-safety` retains its runtime diagnostic +and all four safety commands. Existing exact requirements remain 644 Mac / 628 Linux / +540 WASI functions, 325 Mac/Linux / 288 WASI parameter invocations, 45 Mac / 44 Linux +sharing functions and all 49 core cap probes. Native QA and fresh hosted Linux/native +checks qualify the changed source; earlier static-corpus results are baseline evidence. + +Current-main composition/navigation integration and combined-source G8 review remain +separate. No sibling checkout, protection, tag, release or documentation deployment +is changed by the repair. diff --git a/Documentation/Architecture/Release1.6/G8Integration.md b/Documentation/Architecture/Release1.6/G8Integration.md new file mode 100644 index 0000000..df60a61 --- /dev/null +++ b/Documentation/Architecture/Release1.6/G8Integration.md @@ -0,0 +1,42 @@ +# G8 combined-source reconciliation + +The S3 caller-lifetime repair ends at `8d0e83037cb1e82b33e1dec862123c890b9b9bc7`. +Its standalone Native run 38824896580 and ARM64/x86_64 Linux run 38824893590 passed, +including ASan/LSan without suppressions. Those receipts and the earlier G0–G7 +records remain immutable evidence of their named source. + +This successor integrates main `7d72c8825982550f0ee87218136fda12d8bae13b`: +composition #35 and Navigation #38. It changes neither implementation’s semantics. +No sibling branch or checkout is modified. The feature-branch integration does +not merge PR #39 into main, tag, publish or deploy documentation. + +## Conflict decisions + +- Preserve all nine products: eight libraries and swift-json. JSONSchemaExamples + depends only on JSON/JSONSchema; Navigation only on JSON. Core imports neither. + The root dependency graph stays empty and private runtime targets stay private. +- Union exact root, shared-state, coverage and WASI inventories. Composition adds + 12 integration functions; Navigation adds 39 functions and one sharing function. + All 98 S3 functions, four sharing functions, 126 restored maintenance identities, + per-function parameter cardinalities and 49 baseline cap probes remain required. +- Preserve focused composition, Navigation, S3 ownership/public-consumer/matrix + and lifetime probes. The composition five-success/three-refusal capacity policy + remains explicit. No quota, sanitizer, exclusion, timeout or review trigger changes. +- Union eight DocC catalogs, routes, public API/resource descriptions and original + contribution notices. Keep stage-specific qualification history source-bound. +- Recompute CLI identity from the combined Package.swift and Sources bytes. + Neither parent’s old generated stamp describes this successor. + +The exact combined requirements are 695 Mac / 679 Linux / 591 WASI root functions, +325 Mac/Linux / 288 WASI parameter invocations and 47 Mac / 46 Linux TSan functions. +These are inventories, not claims that an unexecuted lane passed. Required check +names and protection settings remain unchanged. All native/safety/docs/archive and +both Linux architecture receipts must bind to this combined source. Available WASI +and Apple consumer checks supplement those gates. G8 acceptance and remaining +limitations are recorded with the final source/archive and materialized receipts; +Progress.json is updated only from actual outcomes. + +Logical retention remains distinct from process RSS. No controlled throughput, +caller instruction or arbitrary-schema completeness claim is added. Physical iPad +qualification retains the previously accepted simulator policy. Tagging and release +publication remain separate owner decisions. diff --git a/Documentation/Architecture/Release1.6/G8Qualification.md b/Documentation/Architecture/Release1.6/G8Qualification.md new file mode 100644 index 0000000..8282cbb --- /dev/null +++ b/Documentation/Architecture/Release1.6/G8Qualification.md @@ -0,0 +1,76 @@ +# G8 combined-source qualification and review + +Reviewed source `130823a5c8af47e21251f271df50a948c73cb9a1` integrates main +`7d72c8825982550f0ee87218136fda12d8bae13b` with repaired S3 parent +`8d0e83037cb1e82b33e1dec862123c890b9b9bc7`. The actual PR merge-ref source is +`34efc30e56da30a1e907a8f7d8b6614fd97b2951`; every public source-input hash matches. +This record is a documentation-only successor. Final PR checks and materialized +receipts bind its final archive separately; executable inputs and declarations stay +identical. Earlier G0–G7 and lifetime-repair records are retained as history. + +[Native run 38829233646](https://github.com/ThorFuchs/Swift-JSON/actions/runs/38829233646) +and [Linux run 38829233447](https://github.com/ThorFuchs/Swift-JSON/actions/runs/38829233447) +passed all eight qualification jobs. Branch Pages deployment is skipped. Commands +run through `Scripts/ci.py`; full native/Linux jobs additionally run `--lane composition`. +All actual commands, exits, event streams, source-input hashes and environments are +materialized in the maintainer review bundle; expected compiler failures remain +explicit nonzero outcomes, not silently ignored failures. + +| Actual scope | Outcomes | +| --- | --- | +| Native lane | 38 commands; 695 functions and 325 parameter invocations in each of Debug/Release | +| Native safety | Four commands; ASan 695/325, TSan 47, strict-memory warnings-as-errors, 128-scope corpus lifetime probe | +| Linux, each architecture | 34 commands; 679/325 Debug/Release; pinned Swift6.4 Ubuntu24.04 image | +| Linux safety, each architecture | Five commands; ASan 679/325, TSan46, strict-memory, explicit ASan/LSan lifetime probe without suppressions | +| Composition, each full job | Nine commands; 12 core functions, five external functions Debug/Release/ASan, eight cells (five completed, three operational refusals), 11 compiler negatives | +| Native documentation | Eight catalogs, 1,077 authored declarations documented, 14 README fences; S3 ten guide snippets in two processes | +| Native archive | Byte-equal archive/rebuild/extracted rebuild; full 38-command native from a real Git-free extraction | +| Linux extraction, each architecture | Release build, six focused integrity/resource functions and real public consumer; full 679-function root runs are separate | +| WASI | Matching Swift6.4 SDK; 79 commands; Node Debug/Release591 functions and288 parameters; strict memory, 49 caps, public vectors and independent suites; WasmKit public scope remains distinct | +| Apple mobile | S3 and Navigation compiled for both mobile SDKs; iPhone/iPad simulators each ran15 public cases, ten guides and512 matrix certifications with64 negatives | + +Native hosted execution used macOS27.0.1 arm64, Xcode27.2/SDK27.2 and +Apple Swift6.4 (`swiftlang-6.4.0.34.1`); local checks used macOS27.0 with the +same compiler/SDK baseline. Linux ARM64/x86_64 used official Swift6.4 on +Ubuntu24.04.5/glibc. WASI used the installed matching Swift.org6.4 SDK and both +recorded Node/WasmKit runtimes. No baseline, timeout, exclusion, cap or review +trigger was weakened. + +The pinned schema suite still distinguishes 2,300 evaluated successes from37 +policy cases (2,337 observed); JSONTestSuite287 from31 (318 observed). All126 +restored maintenance identities and49 cap probes remain required. Policy cases +are not successes. The amended44-schema/155-witness fixture remains unchanged. +Native fixed replay has816 certifications and32 explicit incompletes, plus two +separate trivial proofs; the additional seeded grammar has512 certifications, +64 negative cases and zero exact external-oracle disagreements. Five current +platform/engine matrix outputs match preserved values, traces, attempts, digests +and charged work byte-for-byte. Incompletes do not prove unsatisfiability. + +## Review decisions and boundaries + +The [12 conflict decisions](G8Integration.md) preserve composition, Navigation, +S3, every parent provenance notice, finite policy and required check name. Source +blob checks prove the S3 implementation/tests/fixtures/consumer match its repaired +parent, and core JSON/composition/Navigation match main. Compiler graphs preserve +all60 S3 declarations and all existing repaired-parent declarations. Navigation's +56 declarations remain unchanged. Nine products and23 targets retain an empty +root dependency graph; runtime targets remain private. CLI identity is regenerated +from the actual combined Package.swift/Sources manifest. + +The source/API/resource/lifecycle review found no further runtime repair needed. +Certification still requires a complete original-root native verdict. False +checks retain work; operational failures and cancellation remain terminal. Exact +numbers/scalars, immutable offline plans, iterative construction and finite +shared ledgers remain authoritative. No new public composition adapter is implied. + +This is agent review, not independent human approval. The physical iPhone was +disconnected, so no new device pass is claimed; its historical G7 receipt retains +its own source boundary. Physical iPad retains the previously accepted simulator +policy. Logical retention is not RSS, functional timing is not controlled +throughput, and arbitrary-schema solving remains incomplete. Source archive +`ab4cc31d95d3409d7947aafa057954089961fd6ff65e573e43ce92dd03f2a29c` +contains1,177 public source files plus its manifest and was independently reproduced. +The final documentation-only successor receives its own archive and receipts. + +G8 is ready for owner review with those explicit limits. PR #39 remains draft; +merging, tagging, release publication and Pages deployment remain separate. diff --git a/Documentation/Architecture/Release1.6/Plan.md b/Documentation/Architecture/Release1.6/Plan.md new file mode 100644 index 0000000..6758771 --- /dev/null +++ b/Documentation/Architecture/Release1.6/Plan.md @@ -0,0 +1,88 @@ +# Release 1.6 — S3 schema examples + +The proposed addition is an opt-in `JSONSchemaExamples` library. It constructs +deterministic candidate JSON and returns a witness only after the existing native +validator completes with `isValid == true`. Incomplete search, unsupported +construction, sound trivial unsatisfiability and operational errors stay distinct. +No arbitrary-schema completeness or unsatisfiability claim is intended. + +The isolated branch is `codex/1.6-schema-examples`, pinned to main +`507dbf9ad4b545a5d60d7d6acf6b814795ec3099`. See +[source reconciliation](Reconciliation.md), [decisions](Decisions.md) and +[progress](Progress.json). The initial orientation and compiler probes are complete. G1 now has a working +explicit-candidate API, a package-only qualified plan bridge and a real public +consumer; see [the accepted declarations and scope](G1API.md). [G1 native qualification](G1Qualification.md) and [G2 determinism](G2Qualification.md) are preserved. G3 now implements [conservative typed construction](G3Construction.md); [its native qualification](G3Qualification.md) is complete. General schema solving remains outside this product. + +## Integration boundary + +Current main has six library products and `swift-json`. S3 will add one library +with dependencies on JSON and JSONSchema only. Existing products gain no dependency +on it. Private regex/filter/CLI targets, empty root dependencies, Swift tools/compiler +6.4, language mode 6, Apple 27 minima, Apache-2.0 and notices remain authoritative. +Dual DSL PR #31 is independent and is not a build or qualification prerequisite. +No DSL integration example is promised until those products actually land. + +## Reviewable increments + +| Gate | Work and acceptance | +| --- | --- | +| G0 | Pin release/main/worktrees, inspect compiled APIs, verify packet and fixture expectations, identify package boundaries | +| G1 | Review exact public declarations; add optional target/test target; implement the smallest qualified read-only plan bridge and real public consumer | +| G2 | Metered splitmix64-v1, exact ordering/construction, versioned corpus/snapshot transport and SHA-256, confined invocation state | +| G3 | Mandatory final certification, foundational 24 must-generate cases and two sound trivial proofs, keyword capability ledger | +| G4 | Ten additional must-generate cases, eight honest incomplete-search cases, qualified refs/composition/regex boundaries | +| G5 | One cumulative ledger across construction and all validation attempts, including completed false verdicts; overflow, retention, terminal failure and cancellation tests | +| G6 | Compiled public examples, DocC, README/API/limits documentation and optional-product isolation | +| G7 | Swift Testing Debug/Release, exact inherited discovery and all 49 caps, safety, pinned suites, determinism and applicable platform qualification | +| G8 | Source/diff/API/capability ledger, actual receipts and fixture outcomes, reproducible source and materialized review return | + +Keep G1/G2/G3 cohesive local commits. Split later reference/search/resource work +when its diff needs separate review. No passing scaffold tests or unimplemented +public success API should be introduced. The amended fixture set has 155 witnesses; +its 44 case classifications and generator acceptance gates are unchanged. + +The current harness requires 419 native functions, 325 parameter invocations and +15 shared-state TSan functions before S3 adds tests. These are current-source +requirements, distinct from the observed 1.5.0 counts of 395/199/14. Preserve +exact identities/cardinalities, all 49 metric probes and independent suite policies. +Fresh S3 qualification must cover its actual final source; old evidence is a baseline. + +Release notes/version metadata, tagging, publication and public activation are +later deliberate steps. This kickoff does not authorize or perform them. E11/E12, +AI/model services, importers, CLI expansion and complete regex inversion stay outside S3. + + +G2 implements `s3-candidates-v2`: metered SplitMix64, stable exact construction, +explicit versioned corpus/source/policy/complete-trace transports and compiler +ownership boundaries. See G2Determinism.md and the subsequent qualification record. +The isolated branch retains its G0/G1 base. Main independently advanced to 037e735 +with 1.5.1 repairs; rebasing/merging that train is a separate integration task +before final G7/G8 release qualification, not silently part of G2. + +G3 advances the candidate algorithm to `s3-candidates-v3` and complete trace to +`swift-json-example-trace-v2`; source/corpus/policy v1 transports are unchanged. +The [capability ledger](Capabilities.json) distinguishes the 24 G3 must-generate +cases and two sound proofs from the 18 remaining G4 cases. All returned witnesses +still require complete validation against the original immutable compiled root. + +G4 implements [conservative advanced proposals](G4Construction.md): contains +minima and bounded anchored ASCII patterns. The ten advanced required and eight +may-fail fixtures receive separate per-case native evidence. [G4 native qualification](G4Qualification.md) is complete; G5-G8 and main repair integration remain separate. + +G5 qualifies [cumulative resources and lifecycle](G5Resources.md), with a corrected +trace reservation, terminal latching at the invocation boundary and 17 additional +behavior cases. See [the native outcomes](G5Qualification.md). Candidate algorithm +v4 and all trace/source/corpus/policy bytes stay unchanged; G6-G8 remain open. + +G6 synchronizes all ten Swift blocks in five S3 DocC guides with a real public +consumer and compiles the new README fence in the seven-library consumer. The +[current API](API.md), [49-metric scope](SchemaExamplesLimits.md), inline comments +and guides describe the implemented subset and lifecycle. See [G6 qualification](G6Qualification.md) +and [documentation contract](G6Documentation.md). Runtime/defaults remain unchanged; +G7/G8 and main repair integration remain open. The conditional DSL example waits +for an available integrated product without touching its independent train. + +G8 reconciles composition and Navigation on main7d72c88 while preserving S3 and +all prior increments. The [combined-source qualification](G8Qualification.md) records +actual execution and the source/API/evidence review. It adds no release, merge, +physical-device or controlled-performance claim beyond its stated scopes. diff --git a/Documentation/Architecture/Release1.6/Progress.json b/Documentation/Architecture/Release1.6/Progress.json new file mode 100644 index 0000000..6f78051 --- /dev/null +++ b/Documentation/Architecture/Release1.6/Progress.json @@ -0,0 +1,437 @@ +{ + "schema_version": 1, + "target_release": "1.6.0", + "wave": "S3 JSONSchemaExamples", + "branch": "codex/1.6-schema-examples", + "initial_release_base": "8a5cf73dfe9b674f7c635f5f6d21a7051d66bb51", + "implementation_base": "507dbf9ad4b545a5d60d7d6acf6b814795ec3099", + "packet_sha256": "f67ec2f5d0bfd223f54a3577f93c03951a7992ecab524910ba44f6bc8621c4a1", + "status": "G8 agent source/API/evidence review complete; combined-source available-platform qualification passed; owner merge/release decisions remain separate", + "release_ready": false, + "gates": { + "S3-G0": "passed_with_documented_fixture_amendment", + "S3-G1": "passed_native_increment", + "S3-G2": "passed_native_increment", + "S3-G3": "passed_native_increment", + "S3-G4": "passed_native_increment", + "S3-G5": "passed_native_increment", + "S3-G6": "passed_native_increment", + "S3-G7": "passed_combined_native_linux_wasi_and_recorded_apple_scopes", + "S3-G8": "review_complete_with_explicit_platform_and_claim_limits" + }, + "observed": { + "packet_manifest_files": 26, + "published_baseline_source_files": 995, + "baseline_hosted_receipts_verified": 7, + "baseline_clean_native_receipts_verified": 1, + "baseline_command_logs_verified": 120, + "baseline_event_streams_verified": 24, + "original_cases": 44, + "original_witnesses": 154, + "original_expected_verdict_errors": 1, + "amended_witnesses": 155, + "amended_native_oracle_disagreements": 0, + "oracle_version": "python-jsonschema 4.26.0 with exact numeric binding", + "oracle_binding_regressions": 8, + "native_prng_golden_draws": 16, + "native_prng_range_checks": 640, + "compiled_package_boundary_probes": 3, + "original_fixture_sha256": "6f2bfd4fdb7c7e206b6570c2156ca8e248831840733b0258aa8cd5938b35649f", + "amended_fixture_sha256": "5b3e6e40e83439a810d0eb7ac412ce158a4e582f36b9d36d6e313c159654ddd3" + }, + "inherited_current_harness_requirements": { + "native_root_functions": 419, + "parameter_invocations": 325, + "native_shared_state_functions": 15, + "core_metrics": 49, + "scope": "Required counts on current main, not a fresh S3 qualification claim" + }, + "next_gate": "Owner review/merge decision; post-merge qualification and release/tag/publication are separate", + "limitations": [ + "Physical iPhone is currently disconnected: no new physical-device pass is claimed; the historical G7 receipt remains source-bound. Fresh SDK and iPhone/iPad simulator checks passed.", + "Physical iPad retains the existing accepted simulator-only policy.", + "General constraint solving, boundary profile and advanced/dynamic construction remain explicitly incomplete.", + "Logical retention is not process RSS; no controlled throughput or caller instruction guarantee.", + "This is agent review and a draft PR; no independent human approval, PR merge, tag, release or deployment is claimed." + ], + "g1_observed": { + "new_swift_testing_functions": 21, + "existing_schema_functions": 69, + "schema_parameter_invocations": 12, + "evaluated_pinned_schema_cases": 2300, + "separate_schema_policy_cases": 37, + "public_consumer_cases": 12, + "expected_compiler_rejections": 2, + "release_address_sanitizer_functions": 21, + "release_thread_sanitizer_functions": 1, + "documented_new_public_declarations": 42, + "compiled_docc_catalogs": 7, + "algorithm": "s3-g1-candidates-v1", + "random_draws_per_invocation": 0, + "focused_native_lane_commands": 6, + "focused_safety_lane_commands": 3 + }, + "current_harness_requirements": { + "native_root_functions": 695, + "linux_root_functions": 679, + "wasi_root_functions": 591, + "parameter_invocations": 325, + "wasi_parameter_invocations": 288, + "native_shared_state_functions": 47, + "linux_shared_state_functions": 46, + "core_metrics": 49, + "scope": "Combined-source exact requirements; execution results remain source-bound" + }, + "g1_implementation_commit": "6c19580dec99c9363bb4b4d1611af8b8a2bab266", + "g1_qualification_record": "Documentation/Architecture/Release1.6/G1Qualification.md", + "g2_base_commit": "8de39d55675180def26184f24b0eeb87feb214bd", + "g2_qualification_record": "Documentation/Architecture/Release1.6/G2Qualification.md", + "g2_observed": { + "generator_algorithm": "s3-candidates-v2", + "rng_algorithm": "splitmix64-v1", + "new_swift_testing_functions": 21, + "total_generator_functions": 42, + "debug_functions": 42, + "release_functions": 42, + "release_address_sanitizer_functions": 42, + "release_thread_sanitizer_functions": 1, + "concurrent_invocations_in_thread_test": 32, + "public_consumer_cases": 15, + "public_consumer_processes": 2, + "public_compiler_rejections": 2, + "implementation_compiler_positive_probes": 1, + "implementation_compiler_rejections": 5, + "independent_transport_profiles": 2, + "policy_generation_caps_perturbed": 49, + "native_prng_golden_draws": 16, + "native_prng_bounded_selections": 640, + "schema_functions_reused_with_unchanged_core_source": 69, + "schema_parameter_invocations": 12, + "evaluated_pinned_schema_cases": 2300, + "separate_schema_policy_cases": 37, + "harness_mutation_cases": 17, + "testing_event_parser_cases": 19, + "focused_determinism_commands": 7, + "focused_safety_commands": 3 + }, + "g3_base_commit": "42a523c6d0a4dab9195c847feb4e47b43803bfd1", + "g3_qualification_record": "Documentation/Architecture/Release1.6/G3Qualification.md", + "g3_observed": { + "generator_algorithm": "s3-candidates-v3", + "trace_algorithm": "swift-json-example-trace-v2", + "new_swift_testing_functions": 19, + "total_generator_functions": 61, + "debug_functions": 61, + "release_functions": 61, + "release_address_sanitizer_functions": 61, + "release_thread_sanitizer_functions": 2, + "concurrent_invocations_per_thread_function": 32, + "g3_must_generate_cases": 24, + "profiles": 2, + "seeds": 4, + "generated_per_process": 192, + "public_acceptance_processes": 2, + "witness_assertions_per_process": 90, + "separate_trivial_proofs": 2, + "remaining_g4_cases_not_counted": 18, + "integer_rounding_grid_cases": 2940, + "public_consumer_cases": 15, + "public_compiler_rejections": 2, + "implementation_compiler_positive_probes": 1, + "implementation_compiler_rejections": 8, + "focused_native_commands": 8, + "focused_safety_commands": 3, + "harness_mutation_cases": 22, + "testing_event_parser_cases": 19, + "unchanged_schema_source_files": 654, + "reused_schema_functions": 69, + "reused_schema_parameter_invocations": 12, + "reused_evaluated_pinned_schema_cases": 2300, + "separate_schema_policy_exclusions": 37 + }, + "g4_base_commit": "3951b99bb0f690098cf8bef61ce872119e4835da", + "g4_qualification_record": "Documentation/Architecture/Release1.6/G4Qualification.md", + "g4_observed": { + "generator_algorithm": "s3-candidates-v4", + "trace_algorithm": "swift-json-example-trace-v2", + "new_swift_testing_functions": 18, + "total_generator_functions": 79, + "debug_functions": 79, + "release_functions": 79, + "release_address_sanitizer_functions": 79, + "release_thread_sanitizer_functions": 3, + "concurrent_invocations_per_thread_function": 32, + "additional_must_generate_cases": 10, + "honest_may_fail_cases": 8, + "profiles": 2, + "seeds": 4, + "g4_invocations_per_process": 144, + "g4_required_successes_per_process": 80, + "g4_may_valid_per_process": 32, + "g4_explicit_incomplete_per_process": 32, + "public_acceptance_processes": 2, + "full_native_fixture_schemas": 44, + "full_native_witness_expectations_per_process": 155, + "g3_required_regression_successes_per_process": 192, + "g3_separate_trivial_proofs": 2, + "public_consumer_cases": 15, + "public_compiler_rejections": 2, + "implementation_compiler_positive_probes": 1, + "implementation_compiler_rejections": 8, + "focused_native_commands": 9, + "focused_safety_commands": 3, + "harness_mutation_cases": 26, + "testing_event_parser_cases": 19, + "classified_compiled_keywords": 59, + "capability_groups": 16, + "unchanged_schema_source_files": 654, + "reused_schema_functions": 69, + "reused_evaluated_schema_cases": 2300, + "separate_schema_policy_exclusions": 37 + }, + "g5_base_commit": "304f900489dc6c24bce862eccf6adc9865df1027", + "g5_qualification_record": "Documentation/Architecture/Release1.6/G5Qualification.md", + "g5_observed": { + "generator_algorithm": "s3-candidates-v4", + "trace_algorithm": "swift-json-example-trace-v2", + "new_swift_testing_functions": 17, + "total_generator_functions": 96, + "debug_functions": 96, + "release_functions": 96, + "release_address_sanitizer_functions": 96, + "release_thread_sanitizer_functions": 4, + "concurrent_invocations_per_sharing_function": 32, + "new_mixed_success_and_cancelled_invocations": 32, + "cumulative_exact_and_one_short_metrics": 7, + "limits_scope_reviewed": 49, + "public_acceptance_processes": 2, + "g3_required_calls_per_process": 192, + "g4_calls_per_process": 144, + "g4_certified_per_process": 112, + "g4_explicit_incomplete_per_process": 32, + "native_fixture_schemas": 44, + "native_witness_expectations_per_process": 155, + "focused_native_commands": 9, + "focused_safety_commands": 3, + "public_consumer_cases": 15, + "public_compiler_rejections": 2, + "implementation_positive_probes": 1, + "implementation_compiler_rejections": 8, + "harness_mutation_cases": 26, + "testing_event_parser_cases": 19, + "unchanged_core_files": 654, + "reused_schema_functions": 69, + "reused_evaluated_schema_cases": 2300, + "separate_schema_policy_exclusions": 37, + "unmatched_trace_refund_removed_bytes": 32, + "default_caps_changed": false, + "public_signatures_changed": false + }, + "g6_base_commit": "30cf344c674a7514db2a578397ade642c0ab942e", + "g6_qualification_record": "Documentation/Architecture/Release1.6/G6Qualification.md", + "g6_observed": { + "generator_algorithm": "s3-candidates-v4", + "public_signatures_or_defaults_changed": false, + "production_swift_files_compared": 181, + "production_doc_comment_only_files": 3, + "root_manifest_and_existing_tests_unchanged": true, + "swift_testing_functions": 96, + "debug_functions": 96, + "release_functions": 96, + "release_address_sanitizer_functions": 96, + "release_thread_sanitizer_functions": 4, + "focused_native_commands": 9, + "focused_safety_commands": 3, + "compiled_s3_guide_snippets": 10, + "s3_guides": 5, + "documentation_processes": 2, + "compiled_readme_fences": 14, + "documentation_harness_mutations": 11, + "existing_ci_harness_mutations": 26, + "testing_event_parser_regressions": 19, + "classified_metrics": 49, + "public_consumer_cases": 15, + "public_access_compiler_negatives": 2, + "ownership_compiler_positive": 1, + "ownership_compiler_negatives": 8, + "g3_required_calls_per_process": 192, + "g4_calls_per_process": 144, + "g4_valid_per_process": 112, + "g4_explicit_incomplete_per_process": 32, + "native_fixture_witness_expectations_per_process": 155, + "conditional_dsl_example": "deferred: product absent from inspected S3 package and main base; sibling train untouched", + "release_ready": false + }, + "historical_current_states": [ + { + "gate": "S3-G6", + "status": "G6 complete: synchronized compiled public guides, current API/README/DocC and all 49 metric scopes native-qualified", + "current_harness_requirements": { + "native_root_functions": 515, + "parameter_invocations": 325, + "native_shared_state_functions": 19, + "core_metrics": 49, + "scope": "Full-lane requirements; focused G5 observations recorded separately" + }, + "next_gate": "G7 main-repair integration, full native/safety/pinned-suite/oracle/platform qualification; G8 final review return", + "limitations": [ + "G7/G8 remain open; this is not a qualified 1.6 release candidate", + "Original packet expectation/oracle representation amendment remains separately preserved", + "No new full nine-bundle Native/safety, Linux, WASI, mobile or controlled-performance pass is claimed", + "Main independently advanced to 037e735 (1.5.1 repairs); this isolated G6 branch preserves its G0/G1 base pending later integration.", + "G2-G6 are native-only qualification: Linux/WASI/mobile goldens remain G7; no controlled throughput or RSS claim", + "General constraint solving, boundary profile and advanced/dynamic construction remain explicitly incomplete", + "Historical exploratory/compiler/test/harness attempts are retained separately from final passed receipts" + ] + }, + { + "gate": "S3-G7-local", + "status": "G7 main repairs reconciled; full Git-free native/safety, oracle and Apple/WASI qualification passed; full Linux native/safety blocked by local storage", + "gates": { + "S3-G0": "passed_with_documented_fixture_amendment", + "S3-G1": "passed_native_increment", + "S3-G2": "passed_native_increment", + "S3-G3": "passed_native_increment", + "S3-G4": "passed_native_increment", + "S3-G5": "passed_native_increment", + "S3-G6": "passed_native_increment", + "S3-G7": "qualified_available_scopes_linux_blocked", + "S3-G8": "not_started" + }, + "limitations": [ + "G7 Linux full native/safety remains blocked by local disk; seven commands including Release build passed before guest interruption (exit137), not a full test pass", + "No local native x86_64 Linux qualification; physical iPad deferred under existing simulator acceptance policy", + "G8 remains open and this increment is not a qualified 1.6 release candidate", + "General constraint solving, boundary profile and advanced/dynamic construction remain explicitly incomplete", + "No controlled throughput, heap/RSS or caller instruction guarantee", + "All exploratory/harness failures remain separate from later passed receipts; no push, PR, release or deployment" + ] + } + ], + "g7_base_commit": "4368947c0db3831da988b9d8626055a38b42a7e3", + "g7_integrated_main_commit": "037e735dc6224789e4d04d2c18d15b8d27440198", + "g7_qualified_source_commit": "6652f9bd65b6f21e1a0c8a22fde5d0ce03b68813", + "g7_qualification_record": "Documentation/Architecture/Release1.6/G7Qualification.md", + "g7_observed": { + "default_git_free_native_commands": 37, + "native_debug_functions": 644, + "native_release_functions": 644, + "native_parameter_invocations": 325, + "release_address_sanitizer_functions": 644, + "thread_sanitizer_functions": 45, + "strict_memory_safety_warnings_as_errors": true, + "s3_functions": 98, + "s3_shared_functions": 4, + "restored_maintenance_function_identities": 126, + "core_public_cap_probes": 49, + "fixed_grammar_schemas": 64, + "certifications_per_process": 512, + "known_invalid_per_grammar": 64, + "separate_randomized_grammar_schemas": 64, + "external_generated_certifications": 1328, + "external_known_invalid": 128, + "external_disagreements": 0, + "amended_fixture_schemas": 44, + "amended_fixture_witnesses": 155, + "oracle_numeric_binding_regressions": 8, + "evaluated_pinned_schema_cases": 2300, + "separate_schema_policy_cases": 37, + "evaluated_pinned_json_cases": 287, + "separate_json_policy_cases": 31, + "wasi_checks": 77, + "node_debug_functions": 540, + "node_release_functions": 540, + "wasi_parameter_invocations": 288, + "mobile_sdk_builds": 2, + "simulator_runtimes": 2, + "physical_iphone_runs": 1, + "cross_platform_byte_equal_matrix_hosts": 6, + "linux_completed_commands": 7, + "linux_guest_command_exit": 137, + "linux_full_native_or_safety_pass": false, + "controlled_performance_claim": false, + "public_signature_or_default_changes": false + }, + "g8_initial_head": "c6291148f9ba31b48b9a879b0eb004087f567c96", + "g8_lifetime_record": "Documentation/Architecture/Release1.6/G8CorpusLifetime.md", + "g8_initial_linux_run": "https://github.com/ThorFuchs/Swift-JSON/actions/runs/38822602731", + "history": [ + { + "gate": "S3-G8-corpus-lifetime", + "source": "8d0e83037cb1e82b33e1dec862123c890b9b9bc7", + "status": "passed_standalone_native_linux_safety_docs_archive", + "native_run": 38824896580, + "linux_run": 38824893590, + "limitations": [ + "These runs do not qualify the later composition/Navigation combination; preserved materialized G8 preparation report provides raw receipts." + ] + }, + { + "gate": "S3-G8-integration-preparation", + "status": "G8 combined-source reconciliation with main; fresh qualification in progress", + "gates": { + "S3-G0": "passed_with_documented_fixture_amendment", + "S3-G1": "passed_native_increment", + "S3-G2": "passed_native_increment", + "S3-G3": "passed_native_increment", + "S3-G4": "passed_native_increment", + "S3-G5": "passed_native_increment", + "S3-G6": "passed_native_increment", + "S3-G7": "reopened_for_combined_source_qualification", + "S3-G8": "review_preparation_in_progress" + }, + "source": "130823a5c8af47e21251f271df50a948c73cb9a1" + } + ], + "g8_integrated_main": "7d72c8825982550f0ee87218136fda12d8bae13b", + "review_ready": true, + "actions": { + "feature_branch_pushed": true, + "draft_pr": 39, + "pr_merged": false, + "tag_created": false, + "release_published": false, + "pages_deployed": false, + "sibling_edits": false + }, + "g8_qualified_source_commit": "130823a5c8af47e21251f271df50a948c73cb9a1", + "g8_qualified_pr_merge_commit": "34efc30e56da30a1e907a8f7d8b6614fd97b2951", + "g8_qualification_record": "Documentation/Architecture/Release1.6/G8Qualification.md", + "g8_source_archive_sha256": "ab4cc31d95d3409d7947aafa057954089961fd6ff65e573e43ce92dd03f2a29c", + "g8_observed": { + "native_commands": 38, + "linux_commands_per_architecture": 34, + "composition_commands_per_full_job": 9, + "native_debug_release_asan_functions": 695, + "linux_debug_release_asan_functions": 679, + "parameter_invocations_each": 325, + "native_tsan_functions": 47, + "linux_tsan_functions": 46, + "restored_maintenance_identities": 126, + "finite_core_cap_probes": 49, + "docc_catalogs": 8, + "authored_documented_symbols": 1077, + "s3_authored_declarations": 60, + "navigation_authored_declarations": 56, + "wasi_commands": 79, + "wasi_debug_release_functions": 591, + "wasi_parameter_invocations_each": 288, + "sdk_builds_s3": 2, + "sdk_builds_navigation": 2, + "simulators": 2, + "fresh_physical_device_runs": 0, + "fixed_native_records": 848, + "fixed_certified": 816, + "fixed_explicit_incomplete": 32, + "separate_trivial_proofs": 2, + "additional_research_certified": 512, + "additional_research_invalid": 64, + "external_oracle_disagreements": 0, + "byte_equal_current_matrix_platforms": 5, + "expected_ownership_rejections": 8, + "public_consumer_cases": 15, + "public_access_rejections": 2, + "compiled_guide_snippets": 10, + "source_review_new_runtime_findings": 0 + } +} diff --git a/Documentation/Architecture/Release1.6/Reconciliation.md b/Documentation/Architecture/Release1.6/Reconciliation.md new file mode 100644 index 0000000..3a36e0e --- /dev/null +++ b/Documentation/Architecture/Release1.6/Reconciliation.md @@ -0,0 +1,104 @@ +# S3 source and input reconciliation + +Observed on 2026-10-15. The private raw receipts and probes are retained under +ignored `Artifacts/SchemaExamplesS3` in the isolated checkout. This record closes +orientation; it does not claim a generator implementation or release qualification. + +## Releases and independent trains + +GitHub reports 1.5.0 published and latest at +`8a5cf73dfe9b674f7c635f5f6d21a7051d66bb51`. The protected 1.4.0 tag points to +`e8c3e4bd9ae40d797f07a527ea98879acee3c057`; its separate release remains draft. +The CLI's frozen `1.4.0-dev`/unknown-revision metadata is an explicitly disclosed +1.5.0 release limitation, not grounds to rewrite published source or old receipts. + +Initial local main and origin/main matched 1.5.0 and were clean. All 23 registered +worktrees, including the newly created S3 checkout, were inspected; no visible +local changes were found at that observation. Historical qualification worktrees +were retained. E2 and the CLI are already integrated into main; old branch ancestry +alone is not evidence that squash-merged features remain unintegrated. + +During orientation, the sibling session merged HTML previews PR #30 as +`507dbf9ad4b545a5d60d7d6acf6b814795ec3099`. Only the new, still-clean S3 branch +was fast-forwarded before its first tracked edit. Original main was left at +1.5.0. JSON, JSONSchema and JSONRegexRuntime source trees are byte-identical +between those commits. The manifest delta adds JSONHTMLTests fixture resources; +the seven products, empty dependency graph and JSON/schema target edges are unchanged. +The HTML/CLI behavior delta and its current CI belong to that separate merged train. + +Dual DSL PR #31 remains draft at `64264218dc0073a5738ab557bfa25b3bb9f6fa7a` +and reports conflicts after main advanced. Neither its files nor its state were +changed. S3 imports neither proposed DSL product. Future main movement requires +an explicit integration check; this feature does not continuously reset to it. + +Strict required checks remain `Native / native`, `Native / safety` and +`Native / docs`, with PRs, resolved review threads and no bypass. No protection, +tag, release or remote branch configuration was changed by this kickoff. + +## Actual baseline evidence + +The downloaded published source is SHA-256 +`b3ec3ede09b448a32dceaf8ba89f97f99458e3dade9e3c4236d043e30e1d4389`, +1,501,990 bytes, 995 source files plus manifest. A local sorted normalized archive +of the untouched initial checkout reproduced those exact bytes. + +The materialized 1.5.0 evidence archive is +`afe7ac0ebd07c7fe963f9f042db090a6e547e9abbdd2898bf174a9d7fc528a76`. +Verification covered 455 manifested evidence files, seven hosted CI receipts and +one clean-extraction native receipt, 120 command logs and 24 structured event +streams. All 995 source hashes matched the released checkout. This reuses actual +passed baseline evidence; no new uninterrupted full Native/S3 lane is inferred. +Linux receipts omit direct Git SHA; their complete source hashes and published +workflow identity remain the documented identity evidence. + +This machine passed native preflight: macOS 27.0 arm64, Xcode 27.2 build 27B5019j, +Apple Swift 6.4 (swiftlang-6.4.0.34.1), SDK 27.2, language mode 6. Probes explicitly +select the matching Xcode/SDK, complete strict concurrency and warnings as errors. +No global developer-directory selection or compatibility flag was introduced. + +## Packet and fixture amendment + +Packet SHA-256 is +`f67ec2f5d0bfd223f54a3577f93c03951a7992ecab524910ba44f6bc8621c4a1`; +all 26 non-manifest files verified. Its inspected baseline was 9517233, before +the merged E2, CLI/provider and structured-preview increments. It contains design +proposals and research receipts, not implemented generator source. + +The unmodified Python 4.26.0 oracle passes its 44 cases/154 witnesses. Native replay +found one incorrect expectation: `exact-integer-beyond-2pow53/invalid_3` marks +numeric `9007199254740994.0` invalid for integer bounds 9007199254740993 through +9007199254740995. It is mathematically integral and within those bounds. This agrees +with [Draft 2020-12 type semantics](https://json-schema.org/draft/2020-12/json-schema-validation#section-6.1.1). + +The packet parses fractional tokens as Decimal, while python-jsonschema's default +integer binding excludes Decimal. The [oracle's own type-checking documentation](https://github.com/python-jsonschema/jsonschema/blob/main/docs/validate.rst) +describes this Python representation boundary. An exact research decoder now maps +only exactly integral Decimal tokens to exact Python int; other fractions remain +Decimal. No binary floating-point conversion is used. Eight separate binding +regressions include integral lexemes, negative zero, large values, a nonintegral +large value and Boolean exclusion. + +The original packet and its failing native comparison remain immutable. A separate +derived fixture corrects that verdict to true and adds `9007199254740994.5` as a +genuinely fractional negative. All original case classifications remain: 24 G3 +must-generate, 10 G4 must-generate, 8 G4 may-fail-explicitly and 2 G3 sound proofs. +All 155 amended witnesses agree between native Swift 6.4 and the exact-bound Python +oracle. This is fixture validation, not 155 generated examples or proof of completeness. +Original fixture SHA-256 is +`6f2bfd4fdb7c7e206b6570c2156ca8e248831840733b0258aa8cd5938b35649f`; +the derived hash and exact amendment are in the private receipt. + +## Compiled source boundaries + +A separate public-only consumer compiles against real JSON/JSONSchema, retains a +checked-Sendable plan/registry pair, exercises explicit registry resources, native +report and direct validation, and normalized scalar-order writer roundtrips. +Initial probe accessor/autoclosure errors and the original fixture failure are +retained separately from successful amended runs. + +Compiler-driven package probes pass with the actual SwiftPM package identity: +shared noncopyable meter validation, accounting-domain changes and portable +SHA-256 are accessible. Compiled node arrays are package-visible, but node.raw and +reference/child fields are internal. Intended negative probes fail for internal +node access and external package-plan access. G1 therefore needs a narrow +read-only qualified-node bridge, not a guessed URI resolver or broad graph exposure. diff --git a/Documentation/Architecture/Release1.6/SchemaExamplesLimits.md b/Documentation/Architecture/Release1.6/SchemaExamplesLimits.md new file mode 100644 index 0000000..dd7a1be --- /dev/null +++ b/Documentation/Architecture/Release1.6/SchemaExamplesLimits.md @@ -0,0 +1,49 @@ +# Schema example resource scope + +The current API uses the existing 49 finite metrics and version 1 logical weights. +Defaults in [ResourceProfiles.json](../ResourceProfiles.json) are unchanged. This +mapping describes when metrics apply, rather than claiming each metric charges +work in every generation. [G5 resources](G5Resources.md) defines ownership and +terminal behavior; [the compiled guide](../../../Sources/JSONSchemaExamples/Documentation.docc/PoliciesAndLimits.md) +demonstrates policy and checked remaining work using public APIs. + +| Metrics | Scope in schema examples | +| --- | --- | +| workUnits, numericWorkUnits | Cumulative preparation work, then a separate cumulative generation ledger; generation includes rejected candidates, native validations and final hash | +| evaluationWorkUnits, aggregateRegexWorkUnits | Cumulative over every native evaluation in one generation; included in workUnits | +| compileWorkUnits, compiledPlanBytes | Preparation compiler-domain work and logical reservations, including preparation serialization/hashing scratch | +| schemaDocuments, schemaBytes | Preparation root/registry admission and snapshot capacities | +| schemaNodes, referenceEdges | Preparation compilation plus bounded generation declaration/conjunction/reference discovery | +| retainedBytes | Conservative logical live reservations across all owned phases; release discarded ownership, never work; report certification and peak separately | +| inputBytes, outputBytes, nodes, aggregateTextBytes | Capacity per materialization; retire between values while preserving lifetime report totals and work | +| maxDepth, objectMembers, arrayItems | Before container/frame growth and each value's container cardinality; not lifetime generated-depth totals | +| stringBytes, keyBytes, numberDigits, numberTokenBytes, absoluteExponent | Individual exact token/value capacities and checked synthesis scratch; no implicit dense unlimited exponent expansion | +| evaluationFrames | Bounded pending discovery/construction and native evaluation state | +| issues, diagnosticBytes | Native diagnostic charges remain cumulative after false verdicts; diagnosticBytes also bounds complete abstract trace bytes | +| annotationEntries, annotationBytes | Native evaluated-location/output state; discarded live annotation bytes can be released while work/entry admission stays charged | +| regexPatternBytes | Preparation/native regex pattern admission and generation proposal-scanner source capacity | +| aggregateRegexPatternBytes, regexProgramInstructions, regexCompileWorkUnits, regexCaptureGroups | Native preparation regex compilation; not a new generation matcher or compiler | +| regexSearchWorkUnits, regexRetainedBytes | Native per-search ceiling and logical live state; per-search retirement does not reset aggregateRegexWorkUnits | +| pointerBytes, pointerTokens | Native compiler/evaluator location and diagnostic Pointer helpers | +| cancellationQuantum | Positive and at most 1,024; entry, owned growth and loop checkpoints also apply | +| sourceMapEntries | No new generator source map; caller-admitted document/source-map ownership precedes preparation | +| chunkBytes, pendingEvents, pendingValues, totalInputBytes, totalRecords, totalWorkUnits, totalScannedNodes, maxReusableBufferBytes | Streaming metrics; generation opens no stream or reusable streaming pool; entries remain finite and policy-bound | +| operations, patchWorkUnits | Patch-only metrics; generation performs no Patch operation | + +`maximumAttempts` is the additional finite search ceiling (default 16), not a 50th +JSONResourceMetric. Limits are inclusive and quota arithmetic is checked. Capacity-only +checks may report zero. Cancellation propagates as CancellationError, separately +from JSONFailure resources and JSONSchemaExampleFailure incomplete search/proofs. + +Preparation and generation are distinct operations. Each owns one ledger and no +nested helper resets cumulative work. A successful result reports certification; +remaining reservations end on transfer or failure. Conservative arena reservations +can exceed actual temporary liveness. Independent peaks must not be added as if they +were a simultaneous live bound. Logical bytes are not RSS, allocator size or retained +caller output; work units are not elapsed time or arbitrary caller instruction quotas. + +For a caller composition spanning several public calls, subtract successful aggregate +work once, preserve subsystem ceilings independently, and stop on an error/cancellation. +A failed call has no successful report; a fresh full quota would lose the cumulative +bound. Native boundary evidence and unchanged default policies do not prove hard RSS +or throughput guarantees. Full 49-probe/platform release execution remains G7. diff --git a/Documentation/Architecture/ResourceLimits.md b/Documentation/Architecture/ResourceLimits.md index 30cefad..56e9f27 100644 --- a/Documentation/Architecture/ResourceLimits.md +++ b/Documentation/Architecture/ResourceLimits.md @@ -122,6 +122,22 @@ retainedBytes. Scalar scanning and immutable plan-local regex sharing remain the qualified choices; no SIMD or prefilter optimization is claimed. Native performance observations are separate from the logical policy in [performance notes](../Performance/README.md). +## Optional schema example generation (1.6 development) + +JSONSchemaExamples uses the same 49 caps and logical weights, with a separate finite +preparation budget and one cumulative generation/evaluation budget per invocation. +All candidates, including false verdicts, share that generation ledger. Generation +limits intersect validation limits without changing evaluator policy; a positive +finite maximumAttempts (default 16) additionally bounds candidate checks. + +The [metric scope](Release1.6/SchemaExamplesLimits.md) distinguishes cumulative work, +per-materialization capacities, logical reservations and metrics for operations the +generator does not perform. The [compiled public guide](../../Sources/JSONSchemaExamples/Documentation.docc/PoliciesAndLimits.md) +demonstrates checked remaining work. Certification statistics include conservative +staging reservations; transfer or failure ends the invocation's ownership. A failed +call returns no successful report. No independent peak sums, RSS/heap hard caps or +caller instruction guarantees are implied. Default profiles are unchanged. + ## Experimental synchronous composition `JSON.withComposition` borrows a noncopyable, non-Sendable facade over one native diff --git a/Documentation/Architecture/StructuralIntelligence/NAV6Reconciliation.md b/Documentation/Architecture/StructuralIntelligence/NAV6Reconciliation.md new file mode 100644 index 0000000..dd018d1 --- /dev/null +++ b/Documentation/Architecture/StructuralIntelligence/NAV6Reconciliation.md @@ -0,0 +1,23 @@ +# NAV6 source reconciliation + +The isolated candidate combines original N2 8313e06 with protected main c79d48c +(schema examples, PR #39). Original #41 and #42 commits remain preserved. No +Inspection or Navigation semantic source changes occur in this reconciliation. +The manifest contains ten candidate products: nine libraries and the CLI. Root +dependencies remain empty. JSONSchemaExamples and its 98 functions remain intact; +Navigation has 47 functions and Inspection retains all seventeen I01–I17 cases. +The combined root inventory is 720 native, 704 Linux and 616 WASI, with 325/288 +parameter invocations unchanged. Shared-state counts are 48 native and 47 Linux. + +Conflicts in manifest consumers, docs catalogue/site lists, CI and discovery are +resolved by retaining both increments. The harness regression fixture expecting +39 Navigation functions is raised to 47 and adds exact Inspection identities; +missing/wrong-module/changed-parameter counterexamples remain required. The CLI +source digest is regenerated from the finalized manifest and Sources. All 49 +metric probes, 126 maintenance identities, composition qualification and schema +example ownership/matrix/lifetime checks remain mandatory. + +These are requirements, not execution claims. Source-bound receipts determine +qualification. This candidate grants no merge, tag or release authority. N2's +public API and prefix/complete row coverage are unchanged; N3 promotion remains +an independent child experiment. diff --git a/Documentation/Architecture/StructuralIntelligence/SnapshotPromotion.md b/Documentation/Architecture/StructuralIntelligence/SnapshotPromotion.md new file mode 100644 index 0000000..bf57585 --- /dev/null +++ b/Documentation/Architecture/StructuralIntelligence/SnapshotPromotion.md @@ -0,0 +1,63 @@ +# NAV6 snapshot public candidate + +This isolated draft descends from original package-only N3 872b5fd and reconciled +N2 37d84e8 (current main c79d48c). It proposes a public facade inside the existing +JSONInspection product; it creates no additional SwiftPM product. No merge or +release is authorized. Original #41/#42 evidence identities remain unchanged. + +## Accepted interface + +Keep JSONStructuralSnapshotBuilder(document:limits:), build(maximumRecords:), +JSONStructuralSnapshot.document/recordCount, record(at:), location/value/sourceSpan +(for:limits:), JSONStructuralRecord and the two existing typed failure cases. +There are no public record/snapshot initializers or setters. The implementation +remains the original package/private algorithm; no scope, meter, reducer, observer, +borrowed cursor or source-rebinding capability becomes public. + +An ordinal is an integer position local to the snapshot method being called, +never a detached node identity. Two sources may both have position 1 with different +values; explicitly selecting each snapshot selects its own original source. No +method accepts a record, handle or replacement document. The immutable document +attachment keeps old snapshots coherent after other values are patched/replaced. + +## Ownership and errors + +Build performs complete physical preorder under Navigation’s one original scope +and meter. Source ownership transfers once after finalized half-open subtree ends; +ordinary read/visit/collect/inspectArray never transfer and preserve refunds. All +three counters (record count, open depth, subtree end) are bounded by arrays and +admitted nodes. Parent assignment uses existing indices; end equals records.count; +ordinal < maximumRecords is checked before append, so count can at most Int.max +without an increment from Int.max. No end/count arithmetic adds unchecked user +integers. Core retention/work increments use checked UInt64 accounting. Supported +wasm32 capacities retain the existing admission policy; no new narrowing occurs. + +Negative maximumRecords precedes admission; zero refuses the real root. No quota, +cancellation, incomplete traversal or finalization failure returns an artifact. +The exclusive context latches owned failures, including second transfer. A direct +package read-scope transfer guard is an implementation primitive; public N3 +construction reaches it only through that guarded context. Post-transfer caller +errors and quota/cancellation still invalidate the scope and publish no result. + +## Costs and qualification + +Overlay v1 remains 128 fixed logical bytes, 96 per record, edge-key UTF-8 bytes, +16 per open frame, plus original source/path costs. Compact record retention is +O(nodes + key bytes), while existing walker full-path storage and copying remain +depth-sensitive. record(at:) is O(1) trusted-memory metadata access. Each location, +value or span lookup is an explicitly separate finite operation with full source +re-admission and core prefix/path/selection work. K lookups pay K admissions. These +are semantic costs, not new performance claims; logical bytes are not heap or RSS. + +All I01–I17 and S01–S24 identities remain. Three new proofs add an independent +28-record oracle, post-transfer terminal/lifetime cases and concurrent lookup +health isolation. Exact discovery rises to 747 native / 731 Linux / 643 WASI, +with 325/288 parameter invocations unchanged, 44 Inspection functions and 50/49 +shared-state functions. Preserve 49 cap probes, 126 maintenance identities, +2-positive/20-negative full codegen, composition and schema-example lanes. Public +qualification adds fourteen external cases, three executable DocC examples and +18 external full-codegen negative outcomes. Requirements become passes only when +actual source-bound receipts say so; SDK compilation is not device execution. + +Read the [compiled guide](../../../Sources/JSONInspection/Documentation.docc/StructuralSnapshots.md) +and [independent consumer](../../../Tools/SnapshotQualification). diff --git a/Documentation/CI/README.md b/Documentation/CI/README.md index 39cf8b0..5678253 100644 --- a/Documentation/CI/README.md +++ b/Documentation/CI/README.md @@ -1,15 +1,43 @@ +# NAV6 snapshot integration + +Retains current main and all N2/N3 cases. Counts below are historical; the +source-bound discovery manifest and receipts determine this candidate’s inventory. + +# NAV6 integrated inspection candidate + +Adds Inspection to schema-example-enabled main: ten products, nine libraries, +720 native / 704 Linux / 616 WASI functions, 48 native / 47 Linux shared functions. +All 49 caps, 126 maintenance identities and schema/composition lanes remain. +These are discovery expectations; receipts establish execution. + +# Combined S3 / composition / Navigation qualification + +The current inventory requires 695 Mac / 679 Linux / 591 WASI functions, +325 Mac/Linux and 288 WASI parameter invocations, 47 Mac / 46 Linux shared-state +functions, eight library DocC catalogs, nine products and all 49 unchanged finite +cap probes. Composition’s external five-function suite, eight classified capacity +cells and 11 ownership rejections remain additive. Native / native also executes +the focused composition lane; both Linux architecture jobs retain it. +The S3 public/ownership/matrix/lifetime gates and Navigation external consumer are +all retained. Counts specify requirements; source-bound receipts establish passes. +See [G8 reconciliation](../Architecture/Release1.6/G8Integration.md). + +The following sections preserve their original increment histories. + +# NAV5 public inspection candidate + # NAV5 package-only snapshot experiment -+ -+This stacked candidate retains the N2 public surface and adds 24 snapshot cases. -+Root function inventories are 646 native, 630 Linux and 542 WASI; parameter case -+inventories remain 325 native/Linux and 288 WASI. Inspection has 41 functions; -+the focused inspection lane selects inspection|snapshot with exact discovery. -+Shared-state inventories add snapshotS19 (45 native, 44 Linux). No cap probe, -+restored maintenance identity, product or public declaration is added by N3. -+The new package-only source-ownership transfer preserves default input refunds; -+full compiler/safety and source-bound receipts remain required. -+ -+## NAV5 public inspection candidate + +This stacked candidate retains the N2 public surface and adds 24 snapshot cases. +Root function inventories are 646 native, 630 Linux and 542 WASI; parameter case +inventories remain 325 native/Linux and 288 WASI. Inspection has 41 functions; +the focused inspection lane selects inspection|snapshot with exact discovery. +Shared-state inventories add snapshotS19 (45 native, 44 Linux). No cap probe, +restored maintenance identity, product or public declaration is added by N3. +The new package-only source-ownership transfer preserves default input refunds; +full compiler/safety and source-bound receipts remain required. + +## NAV5 public inspection candidate The candidate adds JSONInspection as the ninth product (eight libraries plus CLI). Its existing I01-I17 identities, root discovery, 49 cap probes and 126 restored @@ -107,7 +135,7 @@ Pushes to main, pull requests and manual dispatch run four lanes: after execution. The extracted receipt must have null Git identity and the same complete source manifest. - Documentation: public symbol graphs, complete authored API/parameter comments, - executed README examples, six native DocC catalogs with warnings as errors, + executed README examples, seven native DocC catalogs with warnings as errors, native static-hosting transforms and site route/asset checks. Actions are pinned by commit. Jobs have read-only repository permissions and @@ -204,6 +232,58 @@ remain required. The first failed inherited expectations are retained; no blanke nonzero/exit-zero replacement is used. Real receiving-app and physical-device UI observations remain separate from automated SDK/API and portable execution. +## S3 G1 focused native lane + +`python3 Scripts/ci.py --lane schema-examples` checks the exact eight-product +manifest, empty dependencies, all 49 unchanged finite core policies, 21 functions +in JSONSchemaExamplesTests, all 69 existing JSONSchemaTests functions with their +12 exact parameter invocations, and the real standalone public consumer (12 cases and +2 expected package-boundary compiler rejections). This focused lane supplements +the unchanged required Native checks; it does not establish G7 platform release +qualification. It records source hashes, toolchain, commands, exits and events. + +The full root lane now requires 440 functions on Mac (424 on Linux), with the +same 325 parameter invocations. TSan adds one shared-generator function for a +Mac total of 16. New tests are also included in the exact WASI bundle inventory; +changing an expected inventory does not claim a WASI execution pass. All 49 core +cap behavior probes, pinned independent-suite policies and existing discovery +checks remain required in the full lanes. + +`--lane schema-examples-safety` supplements full safety with Release ASan (all +21 G1 functions), TSan (the one shared-generator function) and strict memory-safety +compilation of the new target and dependencies. Native Swift 6.4's observed +`--test-product JSONSchemaExamplesTests` selects the generated test product; this +does not add a public library product or change full-lane cardinalities. No +filtered lane substitutes for the full release/conformance/platform gates. + +The schema-examples consumer qualifier preserves its 15 public cases/two compiler +negatives and additionally requires ten synchronized DocC snippets in two processes. +The docs lane checks those source bytes before compilation. Exact marker/cardinality +checks reject missing documentation outcomes; all 49 baseline cap probes and the +historical G6 requirements (96 generator/515 full-root/19 sharing functions). + + +## S3 G7 integrated qualification + +The pre-composition S3 G7 exact discovery contract was 644 Mac / 628 Linux / 540 WASI functions, +325 Mac/Linux and 288 WASI parameter invocations, 45 Mac / 44 Linux shared-state +functions, and all 49 core cap probes. JSONSchemaExamples contributes 98 functions +and four sharing functions. All 126 restored maintenance identities remain required. +Earlier counts above describe their named historical increments. + +`--lane schema-examples-qualification` composes the advanced lane with the fixed +64-schema public matrix: 512 seed/profile certifications and 64 known-invalid +mutations, replayed in two processes. Full native also runs that matrix and uses +one explicit root consumer scratch path for subsequent compiler/replay checks. +A broad nonzero test count cannot satisfy any lane. Incomplete generation and +independent-suite policy exclusions remain separate from evaluated successes. + +G7 passed full default native from a Git-free extraction, full native safety, +WASI and the recorded Apple scopes. Linux preflight and Release build passed, but +the guest was interrupted for disk pressure before full tests/safety. Those gates +remain open; platform SDK compilation, simulator/device consumers and WASI do not +substitute for Linux results. See the [G7 record](../Architecture/Release1.6/G7Qualification.md). + ## Experimental composition lane `python3 Scripts/ci.py --lane composition` executes 12 composition functions in diff --git a/Documentation/DocumentationTooling.md b/Documentation/DocumentationTooling.md index 357c3de..5291722 100644 --- a/Documentation/DocumentationTooling.md +++ b/Documentation/DocumentationTooling.md @@ -22,10 +22,18 @@ checks README Swift fences against their executable consumer source, runs that consumer, and compiles all eight DocC catalogs with warnings as errors. Zero public symbols, missing comments, stale examples or a failed catalog fail the lane. +The lane also executes the standalone schema-examples consumer (15 preserved cases and two +expected compiler rejections), plus every Swift block in all five JSONSchemaExamples +guides (ten independently scoped snippets, executed in two processes). Every successful consumer value, including the +offline-reference case, receives an independent native verdict and strict +roundtrip check. Remaining work is derived from public limits and cumulative +statistics; the consumer imports no private meters. + | Output | Purpose | | --- | --- | | `.build/handoff/docc/JSON.doccarchive` | JSON API and exact values/native coding guide | | `.build/handoff/docc/JSONSchema.doccarchive` | Schema API and offline validation guide | +| `.build/handoff/docc/JSONSchemaExamples.doccarchive` | Certified candidates, conservative construction, deterministic provenance and finite lifecycle | | `.build/handoff/docc/JSONStreaming.doccarchive` | Streaming API and completion/backpressure guide | | `.build/handoff/docc/JSONHTML.doccarchive` | Exact formatted HTML API and rendering guide | | `.build/handoff/docc/JSONPatch.doccarchive` | Patch API and choosing/applying patches guide | @@ -119,3 +127,25 @@ Run native and safety lanes when code changes; required branch checks also run o public documentation PRs. Preserve published source/evidence identities and create a distinct successor archive when source content changes. Performance evidence is reused only when its measured implementation and workload remain applicable. + +### Keep schema example guides executable + +Every Swift fence in `Sources/JSONSchemaExamples/Documentation.docc/*.md` is counted, +hashed and synchronized with the separate public consumer. The current contract is +ten snippets across five guides, covering exact values/keys, offline definition +selection, deterministic replay, format/default policy, remaining work and explicit +search failure. The docs/native consumer qualifiers require both process completions +and preserve all original 15 consumer cases and two compiler negatives. + +```sh +python3 -B Scripts/schema_example_documentation.py --write +python3 -B Scripts/verify_schema_example_documentation.py +swift run --package-path Examples/SchemaExamplesConsumer SchemaExamplesConsumer --documentation +``` + +Review the generated DocumentationExamples.swift diff after editing a guide. Missing, +extra, empty or foreign-import snippets and source drift fail qualification. The +`#sourceLocation` directives map compiler errors to the authored guide. The root +README has fourteen executable Swift fences in its separate seven-library consumer; +installation fragments remain manifest examples, checked by real package manifests. +Native DocC checks symbol links and the site verifies the new limits-guide route. diff --git a/Documentation/Qualification/TestDiscovery.json b/Documentation/Qualification/TestDiscovery.json index c0f923d..65078da 100644 --- a/Documentation/Qualification/TestDiscovery.json +++ b/Documentation/Qualification/TestDiscovery.json @@ -11,7 +11,8 @@ "JSONBridgesTests": 10, "JSONCLITests": 88, "JSONNavigationTests": 47, - "JSONInspectionTests": 41 + "JSONSchemaExamplesTests": 98, + "JSONInspectionTests": 44 }, "parameterized_invocations": 325, "composition_external_functions": 5, @@ -33,7 +34,8 @@ "JSONBridgesTests": 1, "JSONCLITests": 1, "JSONNavigationTests": 1, - "JSONInspectionTests": 2 + "JSONSchemaExamplesTests": 4, + "JSONInspectionTests": 3 }, "coverage_unmapped": { "Sources/JSON/Pointer/JSONLocationComponent.swift": "declaration-only enum", @@ -50,6 +52,7 @@ "coverage_modules": [ "JSON", "JSONSchema", + "JSONSchemaExamples", "JSONRegexRuntime", "JSONStreaming", "JSONPatch", @@ -72,7 +75,8 @@ "JSONBridgesTests": 10, "JSONCLITests": 88, "JSONNavigationTests": 47, - "JSONInspectionTests": 41 + "JSONSchemaExamplesTests": 98, + "JSONInspectionTests": 44 }, "wasi_root_functions": { "JSONTests": 190, @@ -84,7 +88,8 @@ "JSONHTMLTests": 81, "JSONBridgesTests": 10, "JSONNavigationTests": 47, - "JSONInspectionTests": 41 + "JSONSchemaExamplesTests": 98, + "JSONInspectionTests": 44 }, "wasi_parameterized_invocations": 288, "portable_shared_state_functions": { @@ -97,7 +102,8 @@ "JSONBridgesTests": 1, "JSONCLITests": 1, "JSONNavigationTests": 1, - "JSONInspectionTests": 2 + "JSONSchemaExamplesTests": 4, + "JSONInspectionTests": 3 }, "export_functions": { "JSONSchemaTests": 1, diff --git a/Documentation/README.md b/Documentation/README.md index 84411d4..fe03b5e 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -6,15 +6,16 @@ behavioral contracts and native qualification procedure. ## API references and guided reading -The seven candidate native DocC catalogs combine inline API documentation with focused guides: +The eight native DocC catalogs combine inline API documentation with focused guides: | Product | Catalog source | Guide | | --- | --- | --- | | JSON | [JSON](../Sources/JSON/Documentation.docc/JSON.md) | [Exact values and native coding](../Sources/JSON/Documentation.docc/ExactValuesAndCoding.md) | | JSONSchema | [JSONSchema](../Sources/JSONSchema/Documentation.docc/JSONSchema.md) | [Offline schema validation](../Sources/JSONSchema/Documentation.docc/OfflineValidation.md) | +| JSONSchemaExamples | [JSONSchemaExamples](../Sources/JSONSchemaExamples/Documentation.docc/JSONSchemaExamples.md) | [Verified candidates](../Sources/JSONSchemaExamples/Documentation.docc/VerifiedCandidates.md) | +| JSONBridges | [JSONBridges](../Sources/JSONBridges/Documentation.docc/JSONBridges.md) | [Opt-in native conversions](../Sources/JSONBridges/Documentation.docc/NumericFidelity.md) | | JSONStreaming | [JSONStreaming](../Sources/JSONStreaming/Documentation.docc/JSONStreaming.md) | [Completion and backpressure](../Sources/JSONStreaming/Documentation.docc/CompletionAndBackpressure.md) | | JSONHTML | [JSONHTML](../Sources/JSONHTML/Documentation.docc/JSONHTML.md) | [Formatted rendering](../Sources/JSONHTML/Documentation.docc/FormattedRendering.md) | -| JSONBridges | [JSONBridges](../Sources/JSONBridges/Documentation.docc/JSONBridges.md) | [Numeric fidelity](../Sources/JSONBridges/Documentation.docc/NumericFidelity.md) | | JSONNavigation | [JSONNavigation](../Sources/JSONNavigation/Documentation.docc/JSONNavigation.md) | [Exact traversal](../Sources/JSONNavigation/Documentation.docc/Traversal.md) | | JSONPatch | [JSONPatch](../Sources/JSONPatch/Documentation.docc/JSONPatch.md) | [Choosing and applying patches](../Sources/JSONPatch/Documentation.docc/ApplyingPatches.md) | @@ -42,9 +43,10 @@ Online references track main; tagged releases include matching compiled catalogu ## Consumers, qualification and maintenance +- [Seven-library compiled example](../Examples/Consumer/README.md) +- [Schema example public API](Architecture/Release1.6/API.md), [resource scope](Architecture/Release1.6/SchemaExamplesLimits.md) and [runnable guide examples](../Examples/SchemaExamplesConsumer/README.md) - [Experimental N0/N1 source authority](Architecture/StructuralIntelligence/SOURCE-AUTHORITY.md) and [gated traversal decisions](Architecture/StructuralIntelligence/API-DECISIONS.md); JSONNavigation is not a released product. -- [Six-library compiled example](../Examples/Consumer/README.md) - [Downstream JSON/JSONSchema interface](ConsumerInterface.md) and [runnable public consumer](../Tools/MCPReadiness/README.md) - [Independent-suite policies and qualification](Qualification/README.md) - [Native CI lanes and receipts](CI/README.md) diff --git a/Examples/Consumer/Package.swift b/Examples/Consumer/Package.swift index a47cc7a..8920e87 100644 --- a/Examples/Consumer/Package.swift +++ b/Examples/Consumer/Package.swift @@ -10,6 +10,7 @@ let package = Package( targets: [.executableTarget(name: "Consumer", dependencies: [ .product(name: "JSON", package: "Swift-JSON"), .product(name: "JSONSchema", package: "Swift-JSON"), + .product(name: "JSONSchemaExamples", package: "Swift-JSON"), .product(name: "JSONStreaming", package: "Swift-JSON"), .product(name: "JSONPatch", package: "Swift-JSON"), .product(name: "JSONHTML", package: "Swift-JSON"), diff --git a/Examples/Consumer/README.md b/Examples/Consumer/README.md index 6309264..429d96c 100644 --- a/Examples/Consumer/README.md +++ b/Examples/Consumer/README.md @@ -23,3 +23,8 @@ The sixth optional product, JSONBridges, is imported explicitly for the E2 numer examples. Three synchronized guide fences execute alongside all README examples. Core-only and unsupported-Float compiler rejections are checked separately by Scripts/qualify_ergonomics.py in debug and release configurations. + +The seventh library, optional JSONSchemaExamples, executes the README's native- +certified exact integer example. Adding this external consumer dependency creates +no dependency in JSON or JSONSchema. The standalone SchemaExamplesConsumer additionally +executes every S3 DocC Swift block and its native acceptance matrices. diff --git a/Examples/Consumer/Sources/Consumer/READMEExamples.swift b/Examples/Consumer/Sources/Consumer/READMEExamples.swift index e917147..421fae6 100644 --- a/Examples/Consumer/Sources/Consumer/READMEExamples.swift +++ b/Examples/Consumer/Sources/Consumer/READMEExamples.swift @@ -4,6 +4,7 @@ import JSONBridges import JSON import Foundation import JSONSchema +import JSONSchemaExamples import JSONPatch import JSONStreaming import JSONHTML @@ -98,6 +99,16 @@ func runREADMEExamples() throws { let output = try result.output() precondition(output.objectValue?["valid"] == true) + let exampleSchema = try JSONSchemaDocument(JSONParser().parse( + #"{"type":"integer","minimum":9007199254740993}"# + )) + let exampleGenerator = try JSONSchemaExampleGenerator(document: exampleSchema) + let schemaExample = try exampleGenerator.generate(seed: 42, profile: .minimal) + let exampleMinimum = try JSONNumber(parsing: "9007199254740993") + precondition(schemaExample.validation.isValid) + precondition(schemaExample.value.numberValue == exampleMinimum) + precondition(schemaExample.statistics[.evaluationWorkUnits] > 0) + let patch = try JSONPatch(operations: [ .replace(path: JSONPointer(parsing: "/count"), value: 42) ]) diff --git a/Examples/SchemaExamplesConsumer/CompileNegative/CertificateIsNotConstructible.swift b/Examples/SchemaExamplesConsumer/CompileNegative/CertificateIsNotConstructible.swift new file mode 100644 index 0000000..09a7d55 --- /dev/null +++ b/Examples/SchemaExamplesConsumer/CompileNegative/CertificateIsNotConstructible.swift @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSONSchemaExamples +func forbidden() { _ = JSONSchemaExampleResult() } diff --git a/Examples/SchemaExamplesConsumer/CompileNegative/PlanIsPrivate.swift b/Examples/SchemaExamplesConsumer/CompileNegative/PlanIsPrivate.swift new file mode 100644 index 0000000..874387b --- /dev/null +++ b/Examples/SchemaExamplesConsumer/CompileNegative/PlanIsPrivate.swift @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSONSchema +func forbidden(_ schema: CompiledJSONSchema) { _ = schema.exampleRoot } diff --git a/Examples/SchemaExamplesConsumer/Package.swift b/Examples/SchemaExamplesConsumer/Package.swift new file mode 100644 index 0000000..71e42f9 --- /dev/null +++ b/Examples/SchemaExamplesConsumer/Package.swift @@ -0,0 +1,15 @@ +// swift-tools-version: 6.4 +// SPDX-License-Identifier: Apache-2.0 +import PackageDescription + +let package = Package( + name: "SchemaExamplesConsumer", + platforms: [.macOS("27.0"), .iOS("27.0")], + dependencies: [.package(name: "Swift-JSON", path: "../..")], + targets: [.executableTarget(name: "SchemaExamplesConsumer", dependencies: [ + .product(name: "JSON", package: "Swift-JSON"), + .product(name: "JSONSchema", package: "Swift-JSON"), + .product(name: "JSONSchemaExamples", package: "Swift-JSON") + ])], + swiftLanguageModes: [.v6] +) diff --git a/Examples/SchemaExamplesConsumer/README.md b/Examples/SchemaExamplesConsumer/README.md new file mode 100644 index 0000000..814c524 --- /dev/null +++ b/Examples/SchemaExamplesConsumer/README.md @@ -0,0 +1,78 @@ +# Schema examples public consumer + +This standalone Swift 6.4 package imports only the public JSON, JSONSchema and +JSONSchemaExamples products. It executes 15 cases: strict/exact values, scalar-key +identity, preceding invalid candidates, static/offline references, explicit proof +and incomplete-search failures. It independently validates every success and +roundtrips exact JSON through the strict writer/parser. It uses no private meters. + +```sh +swift run --package-path Examples/SchemaExamplesConsumer SchemaExamplesConsumer +``` + +The two compile-negative fixtures must fail for package access protection and an +inaccessible certificate initializer. `Scripts/qualify_schema_examples.py` builds +the real consumer and typechecks them against its actual modules. Construction remains conservative and natively certified. See the G3/G4 architecture records +and JSONSchemaExamples DocC guide for the current boundary. + +The two true-schema profiles emit `G2_VECTORS` (the retained qualification marker) for the independent Python stdlib +transport oracle. The qualifier executes the binary in a second process and +requires identical value/trace bytes. This is native process repeatability; +Linux/WASI determinism remains a later platform gate. The consumer uses public +corpus, policy and trace identities and no private meters. + + +Run the foundational acceptance matrix from the repository root after building: + +```sh +python3 -B Scripts/ci.py --lane schema-examples-construction +``` + +The separate public G3 mode evaluates 24 must-generate schemas across two profiles +and four seeds (192 completed native certifications per process), 90 fixture +witnesses and two sound proofs. Its qualifier repeats in a second process and +materializes every value/trace/work row. The 18 G4 cases remain outside these +success counts. Fixture hashes and the G0 numeric correction are explicit. + + +Run the advanced acceptance layer after the current public consumer build: + +```sh +python3 -B Scripts/ci.py --lane schema-examples-advanced +``` + +This retains the foundational checks and adds 10 G4 must-generate schemas and +8 may-fail schemas, both profiles and four seeds (144 invocations per process). +Every success is certified, independently validated and strictly roundtripped. +Only explicit unsupported/searchExhausted results are accepted for may-fail cases; +no proof, configuration error, native operational error or cancellation can replace +a required success. The qualifier repeats in two processes and materializes each +outcome, plus native verdicts for all 44 schemas/155 witness expectations. Failures +carry no invented value or successful work report. General inversion, triggered +dependency solving and constructive dynamic scope remain incomplete. + +## Compiled public documentation + +All ten Swift snippets across the five JSONSchemaExamples DocC guides execute as +independent scopes in DocumentationExamples.swift, synchronized byte-for-byte from +the authored guides. The exact examples cover scalar-distinct required/null keys, +large integers, offline resource/definition selection and native revalidation, +contains/pattern proposals, seeded replay, remaining work, unchanged format/default +policy and honest incomplete search. No package-private state is imported. + +```sh +python3 -B Scripts/schema_example_documentation.py +swift run --package-path Examples/SchemaExamplesConsumer SchemaExamplesConsumer --documentation +``` + +The existing qualifier runs this mode twice and requires exactly ten completed +snippets each time, in addition to the preserved 15 cases, two process repeatability +vectors, independent transport oracle and two compiler negatives. Drift and count +failures are rejected, without adding root package dependencies. See the current +[API](../../Documentation/Architecture/Release1.6/API.md), +[49-metric scope](../../Documentation/Architecture/Release1.6/SchemaExamplesLimits.md) +and [compiled policy guide](../../Sources/JSONSchemaExamples/Documentation.docc/PoliciesAndLimits.md). + +JSONSchemaDSL is absent from this S3 source snapshot. Its independent train remains +untouched; a DSL/handwritten equality example waits for an available integrated +product. Native examples do not establish later Linux/WASI/mobile release gates. diff --git a/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/DocumentationExamples.swift b/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/DocumentationExamples.swift new file mode 100644 index 0000000..d995990 --- /dev/null +++ b/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/DocumentationExamples.swift @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: Apache-2.0 +// Generated from every JSONSchemaExamples DocC Swift block; do not edit by hand. +import JSON +import JSONSchema +import JSONSchemaExamples + +func runSchemaExampleDocumentation() throws -> Int { + var completed = 0 + do { + #sourceLocation(file: "Sources/JSONSchemaExamples/Documentation.docc/AdvancedConstruction.md", line: 6) + let document = try JSONSchemaDocument(JSONParser().parse( + #"{"type":"array","minItems":2,"uniqueItems":true,"items":{"type":"string"},"contains":{"pattern":"^SKU-[0-9]{4}$"},"minContains":2,"maxContains":2}"# + )) + let result = try JSONSchemaExampleGenerator(document: document).generate(seed: 42) + precondition(result.validation.isValid) + #sourceLocation() + completed += 1 + } + do { + #sourceLocation(file: "Sources/JSONSchemaExamples/Documentation.docc/DeterministicConstruction.md", line: 8) + let corpus = JSONSchemaExampleCorpus(values: [.string("example"), .null]) + let prepared = try JSONSchemaExampleGenerator( + document: JSONSchemaDocument(JSONParser().parse("true")), + options: .init(corpus: corpus) + ) + let result = try prepared.generate(seed: 42, profile: .demonstration) + precondition(result.validation.isValid) + precondition(result.corpusDigest == prepared.corpusDigest) + precondition(result.randomDraws > 0) + #sourceLocation() + completed += 1 + } + do { + #sourceLocation(file: "Sources/JSONSchemaExamples/Documentation.docc/DeterministicConstruction.md", line: 73) + let prepared = try JSONSchemaExampleGenerator( + document: JSONSchemaDocument(JSONParser().parse(#"{"type":"string","minLength":3}"#)) + ) + let first = try prepared.generate(seed: 42, profile: .demonstration) + let second = try prepared.generate(seed: 42, profile: .demonstration) + precondition(first.value == second.value && first.traceDigest == second.traceDigest) + precondition(first.statistics == second.statistics) + precondition(first.sourceSnapshotDigest == prepared.sourceSnapshotDigest) + precondition(first.policyDigest == prepared.policyDigest && first.corpusDigest == prepared.corpusDigest) + #sourceLocation() + completed += 1 + } + do { + #sourceLocation(file: "Sources/JSONSchemaExamples/Documentation.docc/PoliciesAndLimits.md", line: 14) + var limits = JSONLimits.conservative + limits[.workUnits] = 2_000_000 + var evaluation = JSONSchemaOptions(limits: limits) + evaluation.collectAnnotations = true + let options = JSONSchemaExampleOptions( + limits: limits, maximumAttempts: 4, validationOptions: evaluation + ) + let prepared = try JSONSchemaExampleGenerator( + document: JSONSchemaDocument(JSONParser().parse(#"{"enum":[0,"ok"],"type":"string"}"#)), + options: options + ) + let result = try prepared.generate() + let ceiling = min(prepared.options.limits[.workUnits], + prepared.options.validationOptions.limits[.workUnits]) + let used = result.statistics[.workUnits] + precondition(used <= ceiling) + let remainingWork = ceiling - used + precondition(remainingWork < ceiling && result.attempts == 2) + precondition(result.statistics[.evaluationWorkUnits] > 0) + #sourceLocation() + completed += 1 + } + do { + #sourceLocation(file: "Sources/JSONSchemaExamples/Documentation.docc/PoliciesAndLimits.md", line: 57) + let document = try JSONSchemaDocument(JSONParser().parse( + #"{"const":"not-an-email","format":"email","default":"example@example.com"}"# + )) + let annotated = try JSONSchemaExampleGenerator(document: document).generate() + precondition(annotated.value == .string("not-an-email")) + precondition(!annotated.validationOptions.formatAssertion) + let plan = try JSONSchemaCompiler().compile(document) + let asserted = try plan.validate(annotated.value, options: .init(formatAssertion: true)) + precondition(!asserted.isValid) + precondition(document.raw.objectValue?["default"] == .string("example@example.com")) + precondition(!annotated.compilationOptions.formatAssertion) + #sourceLocation() + completed += 1 + } + do { + #sourceLocation(file: "Sources/JSONSchemaExamples/Documentation.docc/TypedConstruction.md", line: 6) + let document = try JSONSchemaDocument(JSONParser().parse( + #"{"type":"integer","minimum":9007199254740993}"# + )) + let generator = try JSONSchemaExampleGenerator(document: document) + let result = try generator.generate(seed: 42) + precondition(result.validation.isValid) + #sourceLocation() + completed += 1 + } + do { + #sourceLocation(file: "Sources/JSONSchemaExamples/Documentation.docc/TypedConstruction.md", line: 80) + let document = try JSONSchemaDocument(JSONParser().parse( + #"{"type":"object","required":["é","e\u0301"],"properties":{"é":{"const":9007199254740993},"e\u0301":{"const":null},"optional":{"default":42}}}"# + )) + let result = try JSONSchemaExampleGenerator(document: document).generate() + let object = result.value.objectValue + let exact = try JSONNumber(parsing: "9007199254740993") + precondition(object?.count == 2 && object?["é"]?.numberValue == exact) + precondition(object?["e\u{301}"] == .null && object?["optional"] == nil) + #sourceLocation() + completed += 1 + } + do { + #sourceLocation(file: "Sources/JSONSchemaExamples/Documentation.docc/VerifiedCandidates.md", line: 12) + let document = try JSONSchemaDocument(JSONParser().parse( + #"{"const":9007199254740993}"# + )) + let prepared = try JSONSchemaExampleGenerator(document: document) + let result = try prepared.generate(seed: 42, profile: .minimal) + let text = try JSONWriter().writeString(result.value) + precondition(result.validation.isValid && text == "9007199254740993") + precondition(result.attempts == 1 && result.statistics[.evaluationWorkUnits] > 0) + #sourceLocation() + completed += 1 + } + do { + #sourceLocation(file: "Sources/JSONSchemaExamples/Documentation.docc/VerifiedCandidates.md", line: 40) + let parser = JSONParser() + let resource = try JSONSchemaDocument(parser.parse( + #"{"$defs":{"count":{"type":"integer","minimum":9007199254740993}}}"# + ), retrievalURI: JSONSchemaURI("https://example.com/schemas/counts")) + let registry = try JSONSchemaRegistry(documents: [resource]) + let root = try JSONSchemaDocument(parser.parse( + ##"{"$ref":"https://example.com/schemas/counts#/$defs/count"}"## + )) + let prepared = try JSONSchemaExampleGenerator(document: root, registry: registry) + let result = try prepared.generate() + let plan = try JSONSchemaCompiler().compile(root, registry: registry) + let independentlyValid = try plan.validate(result.value).isValid + let exact = try JSONNumber(parsing: "9007199254740993") + let roundtrip = try JSONParser().parse(JSONWriter().write(result.value)) + precondition(independentlyValid && result.value.numberValue == exact) + precondition(roundtrip == result.value) + #sourceLocation() + completed += 1 + } + do { + #sourceLocation(file: "Sources/JSONSchemaExamples/Documentation.docc/VerifiedCandidates.md", line: 72) + let document = try JSONSchemaDocument(JSONParser().parse( + #"{"const":1,"type":"string"}"# + )) + let prepared = try JSONSchemaExampleGenerator(document: document) + var exhausted = false + do { + _ = try prepared.generate() + } catch JSONSchemaExampleFailure.searchExhausted(let attempts) { + precondition(attempts == 1) + exhausted = true + } + precondition(exhausted) + #sourceLocation() + completed += 1 + } + precondition(completed == 10) + return completed +} diff --git a/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/G3Acceptance.swift b/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/G3Acceptance.swift new file mode 100644 index 0000000..7f6eb6b --- /dev/null +++ b/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/G3Acceptance.swift @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: Apache-2.0 +import Foundation +import JSON +import JSONSchema +import JSONSchemaExamples + +private enum G3AcceptanceFailure: Error { case mismatch } +enum G3Acceptance { + static func run(fixturePath: String, manifestPath: String) throws { + // Caller-owned qualification I/O and assertions are separate from the + // generator's one invocation ledger. No private plan or meter is imported. + let data = try JSONParser().parse(Data(contentsOf: URL(fileURLWithPath: fixturePath))) + let manifest = try JSONParser().parse(Data(contentsOf: URL(fileURLWithPath: manifestPath))) + guard let cases = data.objectValue?["cases"]?.arrayValue, + let expectedIDs = manifest.objectValue?["g3_must_generate_ids"]?.arrayValue else { throw G3AcceptanceFailure.mismatch } + var observed: [JSON] = [], results: [JSON] = [], proofs: [JSON] = [], witnessCount = 0 + for value in cases { + guard let c = value.objectValue, c["acceptanceGate"] == .string("G3"), + let id = c["id"]?.stringValue, let raw = c["schema"] else { continue } + let assertion = c["validation"]?.objectValue?["formatAssertion"]?.booleanValue == true + let options = JSONSchemaExampleOptions(validationOptions: .init(formatAssertion: assertion)) + let generator = try JSONSchemaExampleGenerator(document: .init(raw), options: options) + if c["generatorExpectation"] == .string("must-prove-unsatisfiable") { + do { _ = try generator.generate(); throw G3AcceptanceFailure.mismatch } + catch JSONSchemaExampleFailure.provenUnsatisfiable(let proof) { + guard id == "boolean-false" && proof == .falseSchema || id == "not-true" && proof == .notTrue else { throw G3AcceptanceFailure.mismatch } + proofs.append(.string(id)); continue + } + } + guard c["generatorExpectation"] == .string("must-generate") else { throw G3AcceptanceFailure.mismatch } + let plan = try JSONSchemaCompiler(options: options.compilationOptions).compile(generator.document, registry: generator.registry) + let validator = JSONSchemaValidator(options: options.validationOptions) + guard let witnesses = c["witnesses"]?.arrayValue else { throw G3AcceptanceFailure.mismatch } + for witness in witnesses { + guard let instance = witness.objectValue?["instance"], let expected = witness.objectValue?["valid"]?.booleanValue, + try validator.validate(instance, against: plan).isValid == expected else { throw G3AcceptanceFailure.mismatch } + witnessCount += 1 + } + for (name, profile) in [("minimal", JSONSchemaExampleProfile.minimal), ("demonstration", .demonstration)] { + for seed in [UInt64(0), 1, 42, UInt64.max] { + let result = try generator.generate(seed: seed, profile: profile) + let bytes = try JSONWriter(options: .init(keyOrder: .scalar, numberStyle: .normalized)).write(result.value) + let roundtrip = try JSONParser().parse(bytes) + let ceiling = min(options.limits[.workUnits], options.validationOptions.limits[.workUnits]) + guard result.validation.isValid, result.validation.diagnosticsComplete, + try validator.validate(result.value, against: plan).isValid, + roundtrip == result.value, result.statistics[.workUnits] <= ceiling, + result.statistics[.evaluationWorkUnits] > 0 else { throw G3AcceptanceFailure.mismatch } + let row = try JSONObject(members: [ + .init("id", value: .string(id)), .init("profile", value: .string(name)), + .init("seed", value: .number(JSONNumber(integer: seed))), + .init("value", value: result.value), .init("trace", value: .string(result.traceDigest)), + .init("snapshot", value: .string(result.sourceSnapshotDigest)), + .init("corpus", value: .string(result.corpusDigest)), .init("policy", value: .string(result.policyDigest)), + .init("attempts", value: .number(JSONNumber(integer: result.attempts))), + .init("work", value: .number(JSONNumber(integer: result.statistics[.workUnits]))), + .init("evaluationWork", value: .number(JSONNumber(integer: result.statistics[.evaluationWorkUnits]))), + .init("remainingWork", value: .number(JSONNumber(integer: ceiling - result.statistics[.workUnits]))), + .init("peakLogicalRetainedBytes", value: .number(JSONNumber(integer: result.statistics.peakLogicalRetainedBytes))), + .init("nativeCertified", value: true), .init("independentNativeValid", value: true), .init("roundtrip", value: true) + ]) + results.append(.object(row)) + } + } + observed.append(.string(id)) + } + guard observed == expectedIDs, observed.count == 24, results.count == 192, proofs.count == 2, + witnessCount == 90 else { throw G3AcceptanceFailure.mismatch } + let report = try JSONObject(members: [ + .init("algorithm", value: .string("s3-candidates-v4")), .init("ids", value: .array(observed)), + .init("results", value: .array(results)), .init("proofs", value: .array(proofs)), .init("witnesses", value: 90) + ]) + print("G3_RECEIPT " + (try JSONWriter().writeString(.object(report)))) + print("SCHEMA_EXAMPLES_G3 passed cases=24 profiles=2 seeds=4 validated=192 witnesses=90 proofs=2") + } +} diff --git a/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/G4Acceptance.swift b/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/G4Acceptance.swift new file mode 100644 index 0000000..1e791f0 --- /dev/null +++ b/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/G4Acceptance.swift @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: Apache-2.0 +import Foundation +import JSON +import JSONSchema +import JSONSchemaExamples + +private enum G4AcceptanceFailure: Error { case mismatch } +enum G4Acceptance { + static func run(fixturePath: String, manifestPath: String) throws { + // Caller-owned qualification I/O and native oracle calls do not claim + // instruction or memory caps from the generator's own public reports. + let input = try JSONParser().parse(Data(contentsOf: URL(fileURLWithPath: fixturePath))) + let manifest = try JSONParser().parse(Data(contentsOf: URL(fileURLWithPath: manifestPath))) + guard let cases = input.objectValue?["cases"]?.arrayValue, + let requiredIDs = manifest.objectValue?["g4_must_generate_ids"]?.arrayValue, + let incompleteIDs = manifest.objectValue?["g4_may_fail_ids"]?.arrayValue else { throw G4AcceptanceFailure.mismatch } + var results: [JSON] = [], observedRequired: [JSON] = [], observedIncomplete: [JSON] = [] + var witnesses = 0, schemas = 0, mustValid = 0, mayValid = 0, explicit = 0 + for value in cases { + guard let c = value.objectValue, let raw = c["schema"], let id = c["id"]?.stringValue, + let witnessRows = c["witnesses"]?.arrayValue else { throw G4AcceptanceFailure.mismatch } + let options = JSONSchemaExampleOptions(validationOptions: .init(formatAssertion: c["validation"]?.objectValue?["formatAssertion"]?.booleanValue == true)) + var documents: [JSONSchemaDocument] = [] + if let resources = c["resources"]?.objectValue { + for member in resources { documents.append(try .init(member.value, retrievalURI: JSONSchemaURI(member.key.string))) } + } + let document = try JSONSchemaDocument(raw), registry = try JSONSchemaRegistry(documents: documents) + let plan = try JSONSchemaCompiler(options: options.compilationOptions).compile(document, registry: registry) + let validator = JSONSchemaValidator(options: options.validationOptions) + // Evaluate every witness in all 44 cases, including the six negatives + // attached to the two trivial G3 proof cases. These are native checks. + for witness in witnessRows { + guard let instance = witness.objectValue?["instance"], let expected = witness.objectValue?["valid"]?.booleanValue, + try validator.validate(instance, against: plan).isValid == expected else { throw G4AcceptanceFailure.mismatch } + witnesses += 1 + } + schemas += 1 + guard c["acceptanceGate"] == .string("G4") else { continue } + let must = c["generatorExpectation"] == .string("must-generate") + guard must || c["generatorExpectation"] == .string("may-fail-explicitly") else { throw G4AcceptanceFailure.mismatch } + let generator = try JSONSchemaExampleGenerator(document: document, registry: registry, options: options) + for (name, profile) in [("minimal",JSONSchemaExampleProfile.minimal),("demonstration",.demonstration)] { + for seed in [UInt64(0),1,42,UInt64.max] { + var row: [JSONMember] = [.init("id",value: .string(id)),.init("profile",value: .string(name)), + .init("seed",value: .number(JSONNumber(integer: seed))),.init("required",value: .boolean(must))] + do { + let result = try generator.generate(seed: seed, profile: profile) + let bytes = try JSONWriter(options: .init(keyOrder: .scalar, numberStyle: .normalized)).write(result.value) + let ceiling = min(options.limits[.workUnits], options.validationOptions.limits[.workUnits]) + guard result.validation.isValid, result.validation.diagnosticsComplete, + try validator.validate(result.value, against: plan).isValid, + try JSONParser().parse(bytes) == result.value, + result.statistics[.workUnits] <= ceiling else { throw G4AcceptanceFailure.mismatch } + row += [.init("outcome",value: .string("validated")),.init("value",value: result.value), + .init("nativeCertified",value: true),.init("independentNativeValid",value: true),.init("roundtrip",value: true), + .init("trace",value: .string(result.traceDigest)),.init("snapshot",value: .string(result.sourceSnapshotDigest)), + .init("corpus",value: .string(result.corpusDigest)),.init("policy",value: .string(result.policyDigest)), + .init("work",value: .number(JSONNumber(integer: result.statistics[.workUnits]))), + .init("evaluationWork",value: .number(JSONNumber(integer: result.statistics[.evaluationWorkUnits]))), + .init("remainingWork",value: .number(JSONNumber(integer: ceiling - result.statistics[.workUnits]))), + .init("peakLogicalRetainedBytes",value: .number(JSONNumber(integer: result.statistics.peakLogicalRetainedBytes))), + .init("attempts",value: .number(JSONNumber(integer: result.attempts)))] + if must { mustValid += 1 } else { mayValid += 1 } + } catch let failure as JSONSchemaExampleFailure { + guard !must else { throw failure } + switch failure { + case .unsupportedFeature(let feature): + let code: String + switch feature { + case .candidateConstruction: code = "candidateConstruction" + case .dynamicReference: code = "dynamicReference" + case .cyclicReference: code = "cyclicReference" + } + row += [.init("outcome",value: .string("unsupported")),.init("reason",value: .string(code))] + case .searchExhausted(let attempts): + guard attempts > 0 && attempts <= options.maximumAttempts else { throw G4AcceptanceFailure.mismatch } + row += [.init("outcome",value: .string("searchExhausted")),.init("attempts",value: .number(JSONNumber(integer: attempts)))] + default: throw G4AcceptanceFailure.mismatch + } + // No value or successful statistics are invented after error. + explicit += 1 + } + // Core operational errors/cancellation propagate and fail acceptance. + results.append(.object(try JSONObject(members: row))) + } + } + if must { observedRequired.append(.string(id)) } else { observedIncomplete.append(.string(id)) } + } + guard schemas == 44, witnesses == 155, observedRequired == requiredIDs, observedIncomplete == incompleteIDs, + observedRequired.count == 10, observedIncomplete.count == 8, mustValid == 80, + mayValid + explicit == 64, results.count == 144 else { throw G4AcceptanceFailure.mismatch } + let report = try JSONObject(members: [ + .init("algorithm",value: .string("s3-candidates-v4")),.init("requiredIDs",value: .array(observedRequired)), + .init("incompleteIDs",value: .array(observedIncomplete)),.init("results",value: .array(results)), + .init("schemas",value: 44),.init("witnesses",value: 155),.init("mustValid",value: 80), + .init("mayValid",value: .number(JSONNumber(integer: mayValid))),.init("explicit",value: .number(JSONNumber(integer: explicit))) + ]) + print("G4_RECEIPT " + (try JSONWriter().writeString(.object(report)))) + print("SCHEMA_EXAMPLES_G4 passed required=10 incomplete=8 profiles=2 seeds=4 invocations=144 witnesses=155") + } +} diff --git a/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/G7Qualification.swift b/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/G7Qualification.swift new file mode 100644 index 0000000..af2131e --- /dev/null +++ b/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/G7Qualification.swift @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 +import Foundation +import JSON +import JSONSchema +import JSONSchemaExamples + +private enum G7Failure: Error { case mismatch } +enum G7Qualification { + static func run(path: String) throws { + let handle = try FileHandle(forReadingFrom: URL(fileURLWithPath: path)) + let bytes = try handle.readToEnd() ?? Data() + try handle.close() + guard let cases = try JSONParser().parse(bytes).objectValue?["cases"]?.arrayValue, + cases.count == 64 else { throw G7Failure.mismatch } + var count = 0 + for row in cases { + guard let id = row.objectValue?["id"], let raw = row.objectValue?["schema"], + let bad = row.objectValue?["invalid"] else { throw G7Failure.mismatch } + let generator = try JSONSchemaExampleGenerator(document: .init(raw)) + let independent = try JSONSchemaCompiler().compile(generator.document) + guard try !independent.validate(bad).isValid else { throw G7Failure.mismatch } + for (name, profile) in [("minimal", JSONSchemaExampleProfile.minimal), ("demonstration", .demonstration)] { + for seed in [UInt64(0), 1, 42, UInt64.max] { + let result = try generator.generate(seed: seed, profile: profile) + guard result.validation.isValid, result.validation.diagnosticsComplete, + try independent.validate(result.value).isValid, + result.statistics[.evaluationWorkUnits] > 0, + try JSONParser().parse(JSONWriter().write(result.value)) == result.value else { throw G7Failure.mismatch } + let output = try JSONObject(members: [ + .init("id", value: id), .init("profile", value: .string(name)), + .init("seed", value: .number(JSONNumber(integer: seed))), + .init("value", value: result.value), .init("trace", value: .string(result.traceDigest)), + .init("work", value: .number(JSONNumber(integer: result.statistics[.workUnits]))), + .init("evaluationWork", value: .number(JSONNumber(integer: result.statistics[.evaluationWorkUnits]))), + .init("attempts", value: .number(JSONNumber(integer: result.attempts)))]) + print("G7_RESULT " + String(decoding: try JSONWriter(options: .init(keyOrder: .scalar, numberStyle: .normalized)).write(.object(output)), as: UTF8.self)) + count += 1 + } + } + } + guard count == 512 else { throw G7Failure.mismatch } + print("SCHEMA_EXAMPLES_G7 passed schemas=64 certified=512 invalid=64") + } +} diff --git a/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/main.swift b/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/main.swift new file mode 100644 index 0000000..a578f66 --- /dev/null +++ b/Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/main.swift @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSON +import JSONSchema +import JSONSchemaExamples + +private enum ConsumerFailure: Error { case mismatch(Int) } +private func schema(_ text: String, uri: String? = nil) throws -> JSONSchemaDocument { + try JSONSchemaDocument(JSONParser().parse(text), retrievalURI: uri.map { try JSONSchemaURI($0) }) +} + +@main struct SchemaExamplesConsumer { + static func main() throws { + if CommandLine.arguments.count == 3, CommandLine.arguments[1] == "--g7-fuzz" { + try G7Qualification.run(path: CommandLine.arguments[2]); return + } + if CommandLine.arguments.count == 2, CommandLine.arguments[1] == "--documentation" { + let completed = try runSchemaExampleDocumentation() + print("SCHEMA_EXAMPLES_DOCUMENTATION passed snippets=\(completed)") + return + } + if CommandLine.arguments.count == 5, CommandLine.arguments[1] == "--g4-fixtures", CommandLine.arguments[3] == "--g4-manifest" { + try G4Acceptance.run(fixturePath: CommandLine.arguments[2], manifestPath: CommandLine.arguments[4]); return + } + if CommandLine.arguments.count == 5, CommandLine.arguments[1] == "--g3-fixtures", CommandLine.arguments[3] == "--g3-manifest" { + try G3Acceptance.run(fixturePath: CommandLine.arguments[2], manifestPath: CommandLine.arguments[4]); return + } + let samples: [(String, String, UInt64)] = [ + ("true", "null", 1), + ("{}", "null", 1), + (#"{"const":null}"#, "null", 1), + (#"{"const":9007199254740993}"#, "9007199254740993", 1), + (#"{"const":9007199254740994.5}"#, "9007199254740994.5", 1), + (#"{"const":{"é":1,"e\u0301":2}}"#, #"{"é":1,"e\u0301":2}"#, 1), + (#"{"enum":[1,"ok"],"type":"string"}"#, #""ok""#, 2), + (#"{"default":1,"examples":["ok"],"type":"string"}"#, #""ok""#, 2), + (##"{"$defs":{"x":{"const":7}},"$ref":"#/$defs/x"}"##, "7", 1) + ] + var passed = 0 + for (source, expected, attempts) in samples { + let document = try schema(source) + let generator = try JSONSchemaExampleGenerator(document: document) + let result = try generator.generate(seed: 42) + let expectedValue = try JSONParser().parse(expected) + let native = try JSONSchemaCompiler().compile(document).validate(result.value) + let roundtrip = try JSONParser().parse(JSONWriter().write(result.value)) + let workCeiling = min(generator.options.limits[.workUnits], generator.options.validationOptions.limits[.workUnits]) + guard result.value == expectedValue, result.validation.isValid, + result.attempts == attempts, result.seed == 42, + result.statistics[.evaluationWorkUnits] > 0, + result.sourceSnapshotDigest == generator.sourceSnapshotDigest, + native.isValid, roundtrip == result.value, + result.statistics[.workUnits] <= workCeiling else { throw ConsumerFailure.mismatch(passed) } + let remainingWork = workCeiling - result.statistics[.workUnits] + guard remainingWork < workCeiling else { throw ConsumerFailure.mismatch(passed) } + passed += 1 + } + let registry = try JSONSchemaRegistry(documents: [schema(#"{"const":"offline"}"#, uri: "https://e.test/value")]) + let prepared = try JSONSchemaExampleGenerator(document: schema(#"{"$ref":"https://e.test/value"}"#), registry: registry) + let offline = try prepared.generate() + let offlinePlan = try JSONSchemaCompiler().compile(prepared.document, registry: registry) + let offlineValid = try offlinePlan.validate(offline.value).isValid + let offlineRoundtrip = try JSONParser().parse(JSONWriter().write(offline.value)) + guard offline.value == .string("offline"), offline.validation.isValid, + offlineValid, offlineRoundtrip == offline.value else { throw ConsumerFailure.mismatch(passed) } + passed += 1 + do { + _ = try JSONSchemaExampleGenerator(document: schema("false")).generate() + throw ConsumerFailure.mismatch(passed) + } catch JSONSchemaExampleFailure.provenUnsatisfiable(.falseSchema) { passed += 1 } + do { + _ = try JSONSchemaExampleGenerator(document: schema(#"{"const":1,"type":"string"}"#)).generate() + throw ConsumerFailure.mismatch(passed) + } catch JSONSchemaExampleFailure.searchExhausted(attempts: 1) { passed += 1 } + let vectorGenerator = try JSONSchemaExampleGenerator(document: schema("true")) + var vectorRows: [String] = [] + for (name, profile) in [("minimal", JSONSchemaExampleProfile.minimal), ("demonstration", .demonstration)] { + let result = try vectorGenerator.generate(seed: 42, profile: profile) + let bytes = try JSONWriter().write(result.value) + let hex = bytes.map { String($0, radix: 16).count == 1 ? "0" + String($0, radix: 16) : String($0, radix: 16) }.joined() + guard result.validation.isValid, result.corpusDigest == vectorGenerator.corpusDigest, + result.policyDigest == vectorGenerator.policyDigest, + result.generatorVersion == "s3-candidates-v4", result.rngAlgorithm == "splitmix64-v1", + result.corpusDigestAlgorithm == "swift-json-example-corpus-v1", + result.policyDigestAlgorithm == "swift-json-example-policy-v1", + result.traceDigestAlgorithm == "swift-json-example-trace-v2", + result.schemaDigestAlgorithm == "swift-json-schema-snapshot-v1", + try JSONSchemaCompiler().compile(vectorGenerator.document).validate(result.value).isValid else { throw ConsumerFailure.mismatch(passed) } + vectorRows.append("\"\(name)\":{\"trace\":\"\(result.traceDigest)\",\"events\":\(result.traceEventCount),\"draws\":\(result.randomDraws),\"value_hex\":\"\(hex)\",\"trace_bytes\":\(result.statistics[.diagnosticBytes])}") + passed += 1 + } + print("G2_VECTORS {\"snapshot\":\"\(vectorGenerator.sourceSnapshotDigest)\",\"corpus\":\"\(vectorGenerator.corpusDigest)\",\"policy\":\"\(vectorGenerator.policyDigest)\",\(vectorRows.joined(separator: ","))}") + let exactCorpus = JSONSchemaExampleCorpus(values: [try JSONParser().parse(#"{"é":9007199254740993,"e\u0301":9007199254740994.5}"#)]) + let exactGenerator = try JSONSchemaExampleGenerator(document: schema("true"), options: .init(corpus: exactCorpus)) + let exact = try exactGenerator.generate(seed: 42, profile: .demonstration) + guard exact.value == exactCorpus.values[0], exact.value.objectValue?.count == 2, + exact.randomDraws == 1, exact.validation.isValid else { throw ConsumerFailure.mismatch(passed) } + passed += 1 + guard passed == 15 else { throw ConsumerFailure.mismatch(passed) } + print("SCHEMA_EXAMPLES_CONSUMER passed cases=15") + } +} diff --git a/Package.swift b/Package.swift index 55bb707..a2cd93c 100644 --- a/Package.swift +++ b/Package.swift @@ -8,6 +8,7 @@ let package = Package( products: [ .library(name: "JSON", targets: ["JSON"]), .library(name: "JSONSchema", targets: ["JSONSchema"]), + .library(name: "JSONSchemaExamples", targets: ["JSONSchemaExamples"]), .library(name: "JSONStreaming", targets: ["JSONStreaming"]), .library(name: "JSONPatch", targets: ["JSONPatch"]), .library(name: "JSONHTML", targets: ["JSONHTML"]), @@ -28,6 +29,8 @@ let package = Package( .testTarget(name: "JSONBridgesTests", dependencies: ["JSONBridges", "JSON"]), .target(name: "JSONRegexRuntime", dependencies: ["JSON"]), .target(name: "JSONSchema", dependencies: ["JSON", "JSONRegexRuntime"], resources: [.copy("Resources/Draft2020-12")]), + .target(name: "JSONSchemaExamples", dependencies: ["JSON", "JSONSchema"]), + .testTarget(name: "JSONSchemaExamplesTests", dependencies: ["JSONSchemaExamples", "JSON", "JSONSchema"]), .target(name: "JSONStreaming", dependencies: ["JSON"]), .target(name: "JSONPatch", dependencies: ["JSON"]), .target(name: "JSONHTML", dependencies: ["JSON"], exclude: ["Assets"]), diff --git a/README.md b/README.md index b56b908..e1d6d52 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,9 @@ Maintained by [Thorsten Fuchs](https://github.com/ThorFuchs). Swift-JSON follows semantic versioning. Use a released version and retain `Package.resolved` for reproducible builds; `main` tracks ongoing development. -This feature branch also contains an [opt-in structural traversal product candidate](Documentation/Architecture/StructuralIntelligence/N1Promotion.md). -It retains Navigation from protected main and proposes JSONInspection as the ninth -product (eight libraries and the CLI). Inspection reports observed array evidence; -its public-product candidate remains unmerged and unreleased. -Merge and release remain human-gated; the [compiled traversal guide](Sources/JSONNavigation/Documentation.docc/Traversal.md) +This candidate retains current main’s schema examples, composition and Navigation, +and proposes opt-in JSONInspection: nine libraries and the CLI. Inspection and +release remain human-gated. The [compiled traversal guide](Sources/JSONNavigation/Documentation.docc/Traversal.md) describes its many-node scope, exact locations, finite accounting and coverage. Published **1.5.0** provides six libraries and the CLI. This development branch @@ -75,6 +73,7 @@ Choose the products your application needs: | --- | --- | | **JSON** | Exact values, strict parsing/writing, native Codable, Pointer, presence, checked construction, source documents and diagnostics | | **JSONSchema** | Draft 2020-12 compilation/evaluation, offline registries, bounded regex, annotations and raw declaration inspection | +| **JSONSchemaExamples** | 1.6 development: deterministic exact candidates, conservative typed construction and native schema certification | | **JSONStreaming** | Pull syntax events and values, strict NDJSON, root-array elements and awaited output chunks | | **JSONHTML** | Exact HTML and immutable exports since 1.3.0; structured forms/cards and bounded previews on unreleased main | | **swift-json** | Bounded command-line JSON workflows (available since 1.5.0) | @@ -88,7 +87,16 @@ Choose the products your application needs: SwiftPM dependency graph is empty. Qualification and benchmark tools are separate packages. -Linux supports the six libraries and CLI with portable schema integrity +The optional 1.6 development product `JSONSchemaExamples` depends only on JSON and +JSONSchema. It prepares immutable offline resources and offers exact declared hints +and conservative typed candidates. Every returned value has passed native validation +against the original root. The supported subset includes exact numeric/string bounds, +required objects, positional arrays, static references, composition hints, contains +minima and bounded anchored ASCII-pattern proposals. Unsupported or exhausted search +is explicit and does not establish unsatisfiability. See [generate verified examples](#generate-verified-schema-examples-16-development) +and the [compiled public guide consumer](Examples/SchemaExamplesConsumer/README.md). + +The inherited Linux increment adds portable schema integrity for its qualified products without runtime dependencies. Its initial qualification scope is Swift 6.4+, 64-bit Ubuntu 24.04/glibc; see the [Linux contract](Documentation/Architecture/Linux.md) for execution gates and limits. Apple platform requirements remain unchanged. @@ -97,7 +105,9 @@ The separately qualified WASI core scope targets full-language Swift 6.4 `wasm32-unknown-wasip1`, with finite defaults and exact persistence. Its [platform contract](Documentation/Architecture/WebAssembly.md) documents resource packaging, execution hosts, qualification scope and safety limitations. Browser -and Embedded Swift hosting require separate qualification. +and Embedded Swift hosting require separate qualification. JSONSchemaExamples has native +source-bound G1–G7 and caller-lifetime repair evidence. Fresh combined-source +qualification is tracked in [G8 integration](Documentation/Architecture/Release1.6/G8Integration.md). ## Installation @@ -116,7 +126,7 @@ Then add products to your target dependencies: Add `JSONStreaming`, `JSONPatch` and `JSONHTML` and opt-in `JSONBridges` in the same way when needed. A local checkout can use `.package(path: "../Swift-JSON")`. The requirement above accepts compatible 1.x -releases. See the [six-product consumer](Examples/Consumer) for a complete buildable +releases. See the [seven-library consumer](Examples/Consumer) for a complete buildable manifest. The website tracks `main`; release assets include compiled DocC catalogues for the exact released source. @@ -305,6 +315,45 @@ states, captures, matching work and cancellation are budgeted. Exact-spelling pr share immutable storage within a plan. See the [schema and regex contract](Documentation/Architecture/SchemaAndRegex.md) and [public consumer interface](Documentation/ConsumerInterface.md). +## Generate verified schema examples (1.6 development) + +In the isolated 1.6 checkout, add the optional `JSONSchemaExamples` product to your +target alongside JSON and JSONSchema. Existing released-version requirements above +do not promise this development API. The generator is useful for deterministic test +fixtures, documentation samples and schema-driven tooling; it is a conservative +search, with complete native validation required for every returned value. + +```swift +import JSONSchemaExamples + +let exampleSchema = try JSONSchemaDocument(JSONParser().parse( + #"{"type":"integer","minimum":9007199254740993}"# +)) +let exampleGenerator = try JSONSchemaExampleGenerator(document: exampleSchema) +let schemaExample = try exampleGenerator.generate(seed: 42, profile: .minimal) +let exampleMinimum = try JSONNumber(parsing: "9007199254740993") +precondition(schemaExample.validation.isValid) +precondition(schemaExample.value.numberValue == exampleMinimum) +precondition(schemaExample.statistics[.evaluationWorkUnits] > 0) +``` + +Preparation owns a separate finite compilation/snapshot ledger. Each generation +shares one cumulative budget across discovery, staging, invalid candidates, native +validation and final trace hashing. Defaults/examples are hints; no defaults, +requiredness or format assertion are invented. A `$defs`-only document needs an +explicit root `$ref` to the desired definition. Registries remain offline and +immutable. `.demonstration` uses a versioned seeded synthetic corpus, without an +AI service or network access. + +The result binds source, ordered corpus, policies, algorithm and complete trace +identities. Search exhaustion or unsupported construction can occur for satisfiable +schemas. Operational errors and cancellation terminate the call with no candidate +or successful report. Logical retention snapshots certification and conservative +staging reservations; it is not heap size or RSS. Read the [getting-started guide](Sources/JSONSchemaExamples/Documentation.docc/VerifiedCandidates.md), +[supported subset](Sources/JSONSchemaExamples/Documentation.docc/TypedConstruction.md), +[policies and limits](Sources/JSONSchemaExamples/Documentation.docc/PoliciesAndLimits.md) +and [current API contract](Documentation/Architecture/Release1.6/API.md). + ## Apply transactional patches ```swift @@ -456,7 +505,7 @@ It includes unreleased APIs. For the exact released 1.5.0 interface, use its [tagged source documentation](https://github.com/ThorFuchs/Swift-JSON/tree/1.5.0/Sources) or its [compiled DocC asset](https://github.com/ThorFuchs/Swift-JSON/releases/tag/1.5.0). -The [documentation index](Documentation/README.md) links the behavior contracts. The six DocC catalogs +The [documentation index](Documentation/README.md) links the behavior contracts. The eight DocC catalogs provide public API references, focused guides, parameter descriptions and failure contracts, compiled from the native Swift public symbol graphs. @@ -533,7 +582,7 @@ swift build -c release --product swift-json ### Run exact pull queries -The package has six libraries plus the executable, with no external root +The development package has eight libraries plus the executable, with no external root dependencies. Build the selected source version with Swift6.4+ (`swift build -c release --product swift-json`), or use `swift run swift-json help`. Native qualification requires macOS/SDK27+. @@ -579,7 +628,7 @@ Add the JSONNavigation product and import it explicitly. Existing one-node reads remain in JSON. Full source admission precedes traversal option validation. See the [compiled traversal guide](Sources/JSONNavigation/Documentation.docc/Traversal.md) and [real external SwiftPM consumer](Tools/NavigationQualification). This branch -prepares nine products; published 1.5.0 and its seven-product contract are unchanged. +prepares ten candidate products; published 1.5.0 and its seven-product contract are unchanged. ## Inspect array evidence @@ -605,3 +654,12 @@ Full source admission still includes the unobserved suffix. Complete row evidenc can have restricted descendant traversal; structural rectangular width is not numeric matrix or schema admission. See the [compiled evidence guide](Sources/JSONInspection/Documentation.docc/ArrayEvidence.md), [public consumer](Tools/InspectionQualification) and [accounting contract](Documentation/Architecture/StructuralIntelligence/Inspection.md). + +The isolated NAV6 candidate also proposes complete source-attached structural +snapshots inside JSONInspection. Read-only parent/edge records preserve physical +preorder and finalized subtree ranges; every value/path/span lookup uses the +captured document and an explicitly separate finite source admission. It adds no +snapshot product, persistent identity or table certification. See the +[snapshot guide](Sources/JSONInspection/Documentation.docc/StructuralSnapshots.md) +and [public consumer](Tools/SnapshotQualification). This API remains an unmerged, +unreleased draft candidate; published products retain their tagged contracts. diff --git a/Scripts/README.md b/Scripts/README.md index 50d07a0..780fe1e 100644 --- a/Scripts/README.md +++ b/Scripts/README.md @@ -10,11 +10,13 @@ separate from the dependency-free library. Native lanes fail on unsupported host | `python3 Scripts/ci.py --lane native` | Manifests, builds, Swift Testing, consumers and suites | | `python3 Scripts/ci.py --lane safety` | Address/Thread Sanitizer and strict memory safety | | `python3 Scripts/ci.py --lane navigation` | N1 product exact discovery, Debug/Release, strict-memory and real external SwiftPM consumer/compiler/DocC proof; no release action | -| `python3 Scripts/ci.py --lane linux` | Actual Linux preflight, all eight candidate products, exact root/cap/corpus/consumer counts | +| `python3 Scripts/ci.py --lane linux` | Actual Linux preflight, all nine products, exact root/cap/corpus/consumer counts | | `python3 Scripts/ci.py --lane linux-safety` | Linux Address/Thread Sanitizer and strict memory safety | | `python3 Scripts/ci.py --lane wasm` | Matched Swift 6.4 WASI debug/release, exact root/corpus/cap/consumer counts, two-host public parity and structured resource failures | | `python3 Scripts/qualify_linux_reproduction.py --output PATH` | Actual source archive, byte-equal rebuild, clean extraction, build/tests and public consumer | -| `python3 Scripts/ci.py --lane docs` | Seven DocC catalogs, authored API/parameter coverage, executed README examples and verified static Pages site | +| `python3 Scripts/ci.py --lane docs` | Eight DocC catalogs, authored API/parameter coverage, executed README examples and verified static Pages site | +| `python3 Scripts/ci.py --lane schema-examples` | G1 focused native tests, exact optional-product graph and actual public consumer/access-control probes | +| `python3 Scripts/ci.py --lane schema-examples-safety` | G1 Release ASan, shared-generator TSan and strict memory-safety compilation; supplements full safety | | `python3 Scripts/build_documentation_site.py` | Transform already-qualified DocC archives and verify static routes/assets | | `python3 Scripts/qualification_watchdog.py --timeout 900 --output PATH -- COMMAND` | Process-group timeout and native receipt | | `python3 Scripts/archive_candidate.py --output PATH` | Public source archive with exact hashes and byte-equal rebuild | @@ -27,6 +29,10 @@ and original frozen archives stay in ignored `Artifacts/`. `Scripts/qualify_ergonomics.py` is part of native/Linux CI: eight real-module compiler probes and three synchronized, executed public guide fences. +`schema_example_documentation.py` synchronizes all ten S3 DocC Swift blocks with +their public consumer. `verify_schema_example_documentation.py` rejects count/import/ +drift and receipt mutations; the native/docs qualifiers execute the ten blocks in +two actual processes. Root README fences execute in the seven-library consumer. The maintenance repair restores `--lane coverage` / `--lane linux-coverage` as explicit profiling tools, `--lane linux-qa` and `--lane wasm-qa` as scoped execution lanes. Their receipts retain precise scopes; preparation does not establish a pass. diff --git a/Scripts/build_documentation.py b/Scripts/build_documentation.py index 823fdd6..b810bcf 100644 --- a/Scripts/build_documentation.py +++ b/Scripts/build_documentation.py @@ -12,6 +12,7 @@ import tarfile import documentation_catalog +import schema_example_documentation from preflight import collect ROOT = pathlib.Path(__file__).resolve().parents[1] @@ -81,6 +82,7 @@ def main(): receipt['coverage'] = coverage if coverage['errors']: raise ValueError('Incomplete authored public API documentation') + receipt['schema_examples_documentation'] = schema_example_documentation.check() example_source, example_count = documentation_catalog.readme_examples() example = ROOT / 'Examples/Consumer/Sources/Consumer/READMEExamples.swift' if example.read_text() != example_source: @@ -89,6 +91,10 @@ def main(): receipt['readme_examples'] = dict(run(command, output / 'readme-examples.log', 900), executable_fences=example_count) if receipt['readme_examples']['exit_code']: raise ValueError('Compiled README examples failed') + command = ['python3', '-B', 'Scripts/qualify_schema_examples.py', '--output', str(output.relative_to(ROOT) / 'schema-examples-consumer'), '--scratch-path', '.build'] + receipt['schema_examples_consumer'] = run(command, output / 'schema-examples-consumer.log', 900) + if receipt['schema_examples_consumer']['exit_code']: + raise ValueError('Public schema-examples consumer or compiler boundaries failed') for product in documentation_catalog.PRODUCTS: graphs = output / 'symbols' / product if graphs.exists(): diff --git a/Scripts/build_documentation_site.py b/Scripts/build_documentation_site.py index 882b56d..78afe6a 100644 --- a/Scripts/build_documentation_site.py +++ b/Scripts/build_documentation_site.py @@ -22,6 +22,7 @@ SITE_URL = 'https://thorfuchs.github.io' + BASE_PATH + '/' GUIDES = {'JSON': ('Exact values and native coding', 'exactvaluesandcoding'), 'JSONSchema': ('Offline schema validation', 'offlinevalidation'), + 'JSONSchemaExamples': ('Verified exact candidates', 'verifiedcandidates'), 'JSONStreaming': ('Completion and backpressure', 'completionandbackpressure'), 'JSONPatch': ('Choosing and applying patches', 'applyingpatches'), 'JSONHTML': ('Exact interactive HTML', 'interactiverendering'), @@ -31,6 +32,7 @@ DESCRIPTIONS = { 'JSON': 'Exact values, strict UTF-8, native Codable, Pointer, presence and finite resource reports.', 'JSONSchema': 'Draft 2020-12, explicit offline registries, immutable plans, annotations and bounded regex.', + 'JSONSchemaExamples': 'Opt-in deterministic exact candidates and conservative typed proposals, with native offline certification and finite budgets.', 'JSONStreaming': 'Pull events and values, strict NDJSON, root-array completion and awaited output chunks.', 'JSONPatch': 'Transactional RFC 6902 JSON Patch and RFC 7396 Merge Patch with exact value semantics.', 'JSONHTML': 'Exact standalone HTML with offline disclosure/search and native-byte copy/download.', @@ -191,6 +193,13 @@ def main(): for route in [product.lower(), 'documentation/' + GUIDES[product][1]]: if not (site / product / 'documentation' / route / 'index.html').is_file(): raise ValueError('Missing product root or guide: ' + route) + if product == 'JSONSchemaExamples': + guides = ['verifiedcandidates', 'typedconstruction', 'advancedconstruction', + 'deterministicconstruction', 'policiesandlimits'] + for guide in guides: + if not (site / product / 'documentation/documentation' / guide / 'index.html').is_file(): + raise ValueError('Missing schema-examples guide: ' + guide) + receipt['schema_example_guides_verified'] = guides # DocC transforms its emitted routes but retains the original archive # root template. Make the product entry point an explicit static redirect. route = BASE_PATH + '/' + product + '/documentation/' + product.lower() + '/' diff --git a/Scripts/check_schema_example_determinism.py b/Scripts/check_schema_example_determinism.py new file mode 100644 index 0000000..7d3eb95 --- /dev/null +++ b/Scripts/check_schema_example_determinism.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +"""Independent stdlib byte-transport/PRNG oracle for the public native G2 consumer.""" +import argparse +import hashlib +import json +import pathlib +import struct + +ROOT = pathlib.Path(__file__).resolve().parents[1] +MASK = (1 << 64) - 1 + + +def u64(value): + return struct.pack('>Q', value) + + +def field(value): + return u64(len(value)) + value + + +def digest(value): + return hashlib.sha256(value).hexdigest() + + +def draws(seed): + while True: + seed = (seed + 0x9E3779B97F4A7C15) & MASK + z = seed + z = ((z ^ (z >> 30)) * 0xBF58476D1CE4E5B9) & MASK + z = ((z ^ (z >> 27)) * 0x94D049BB133111EB) & MASK + yield z ^ (z >> 31) + + +def vectors(): + snapshot = digest(b'SwiftJSON-schema-snapshot-v1\n' + u64(1) + b'\0' + field(b'') + field(b'true')) + corpus = digest(b'SwiftJSON-example-corpus-v1\n' + field(b'[null,false,0,"example",[],{}]')) + profiles = json.loads((ROOT / 'Documentation/Architecture/ResourceProfiles.json').read_text()) + metrics = sum([profiles[k] for k in ['core', 'schema', 'streaming', 'patch', 'enhancement_limits']], []) + metrics = [dict(m, metric=m.get('metric', m.get('id'))) for m in metrics] + assert len(metrics) == 49 and len({m['metric'] for m in metrics}) == 49 + record = u64(49) + b''.join(field(m['metric'].encode()) + u64(m['balanced']) for m in metrics) + policy = digest(b'SwiftJSON-example-policy-v1\n' + u64(16) + record * 3 + bytes([0, 1, 0, 0]) * 2) + result = {'snapshot': snapshot, 'corpus': corpus, 'policy': policy} + for name, profile in [('minimal', 0), ('demonstration', 1)]: + trace = b'SwiftJSON-example-trace-v2\n' + b''.join(field(s.encode()) for s in ['s3-candidates-v4', snapshot, corpus, policy]) + u64(42) + bytes([profile]) + events = [(1, 0, 0)] + index = 0 + if profile: + raw = next(draws(42)) # Bound six has threshold four; this draw is accepted. + assert raw >= ((1 << 64) % 6) + events += [(3, raw, 6)] + index = raw % 6 + events += [(2, 4, index), (4, 1, 1), (5, 1, profile)] + trace += b''.join(bytes([code]) + u64(a) + u64(b) for code, a, b in events) + values = [b'null', b'false', b'0', b'"example"', b'[]', b'{}'] + result[name] = {'trace': digest(trace), 'events': len(events), 'draws': profile, + 'value_hex': values[index].hex(), 'trace_bytes': len(trace)} + return result + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--consumer-log') + args = parser.parse_args() + expected = vectors() + if args.consumer_log: + lines = pathlib.Path(args.consumer_log).read_text().splitlines() + observed = [json.loads(line.removeprefix('G2_VECTORS ')) for line in lines if line.startswith('G2_VECTORS ')] + assert len(observed) == 1 and observed[0] == expected, (observed, expected) + print('SCHEMA_EXAMPLES_DETERMINISM passed profiles=2 metrics=49') + else: + print(json.dumps(expected, sort_keys=True, indent=2)) + + +if __name__ == '__main__': + main() diff --git a/Scripts/ci.py b/Scripts/ci.py index d2f0ddc..606dea7 100644 --- a/Scripts/ci.py +++ b/Scripts/ci.py @@ -11,7 +11,10 @@ SHARED_TESTS = 'independentCodecFacadesAndExactValuesCanBeSharedAcrossTasks|concurrentSchemaEvaluationsHaveExclusiveDynamicRegexAndAnnotationState|sharedRegexProgramHasExclusiveConcurrentSearchState|immutableIndexedObjectCanBeSharedAcrossTasks|qaSharedPayloadAndRootHaveIndependentConcurrentReadScopes|sha256InputCapacityAndConcurrentInvocationsRemainIndependent|sharedFormattedHTMLRendererHasIndependentConcurrentInvocations|sharedAnnotatedDocumentRendererHasIndependentConcurrentInvocations|sharedExportPreparationsHaveIndependentConcurrentState|applePDFSimultaneousJobsHaveIndependentStateAndCancellation|sharedFilterPlanHasIndependentConcurrentInvocations|structuredSharedRendererPlansHaveInvocationOwnedState|sharedProviderBridgeHasIndependentConcurrentInvocations|e2SharedBridgeInputsHaveInvocationOwnedState|e2SharedCompiledPlanFacadeHasIndependentInvocationState' SHARED_TESTS += '|compositionIndependentScopesCanBeSharedAcrossTasks' SHARED_TESTS += '|qaStreaming|qaE2E(SharedPlan|RootImport|NDJSONImport|AwaitedSink|ExactWorkflow)' -SHARED_TESTS += '|navigationSharedImmutableRootIndependentInvocations|inspectionI17CoverageSpansReplacementAndSharedInvocations|snapshotS19NavigationParityAndIndependentInvocations' +SHARED_TESTS += '|g1SharedPreparedGeneratorHasIndependentConcurrentInvocations|g3SharedTypedGeneratorHasIndependentConcurrentInvocations|g4SharedAdvancedGeneratorHasIndependentConcurrentInvocations|g5SharedPreparationSeparatesConcurrentFailureAndSuccessHealth' +SHARED_TESTS += '|navigationSharedImmutableRootIndependentInvocations' +SHARED_TESTS += '|inspectionI17CoverageSpansReplacementAndSharedInvocations' +SHARED_TESTS += '|snapshotS19NavigationParityAndIndependentInvocations|snapshotV22ConcurrentLookupsUseIndependentAdmissionAndHealth' R0_TESTS = 'readerProbe|indexProbes|duplicateRecognitionPrecedesValueAndClosingSyntax' def snapshot(): @@ -29,6 +32,33 @@ def commands(lane, output): '-Xswiftc', '-strict-memory-safety', '-Xswiftc', '-warnings-as-errors'], ['python3', '-B', 'Scripts/qualify_navigation_reducer.py', '--output', str(output / 'owned-codegen')]] return [c + ['--disable-xctest'] if sys.platform == 'linux' and c[:2] == ['swift', 'test'] else c for c in result] + if lane == 'schema-examples-qualification': + return commands('schema-examples-advanced', ROOT / output) + [['python3', '-B', 'Scripts/qualify_schema_example_matrix.py', '--output', str(output / 'matrix')]] + if lane == 'schema-examples-advanced': + return commands('schema-examples-construction', ROOT / output) + [['python3', '-B', 'Scripts/qualify_schema_example_advanced.py', '--output', str(output / 'advanced')]] + if lane in ('schema-examples-determinism', 'schema-examples-construction'): + # G2 changes only the opt-in generator. Reuse unchanged-core schema receipts; + # keep the broader schema-examples lane and every full release gate intact. + baseline = commands('schema-examples', ROOT / output) + return baseline[:4] + [ + ['swift', 'test', '-c', 'release', '--jobs', '2', '--test-product', 'JSONSchemaExamplesTests'], + ['python3', '-B', 'Scripts/qualify_schema_example_ownership.py', '--output', str(output / 'ownership')], + ] + baseline[5:] + ([['python3', '-B', 'Scripts/qualify_schema_example_generation.py', '--output', str(output / 'generation')]] if lane == 'schema-examples-construction' else []) + if lane == 'schema-examples-safety': + return [ + ['swift', 'test', '-c', 'release', '--jobs', '2', '--test-product', 'JSONSchemaExamplesTests', '--sanitize', 'address', '--scratch-path', '.build/schema-examples-address'], + ['swift', 'test', '-c', 'release', '--jobs', '2', '--test-product', 'JSONSchemaExamplesTests', '--sanitize', 'thread', '--scratch-path', '.build/schema-examples-thread', '--filter', 'g1SharedPreparedGeneratorHasIndependentConcurrentInvocations|g3SharedTypedGeneratorHasIndependentConcurrentInvocations|g4SharedAdvancedGeneratorHasIndependentConcurrentInvocations|g5SharedPreparationSeparatesConcurrentFailureAndSuccessHealth'], + ['swift', 'build', '--target', 'JSONSchemaExamples', '--jobs', '2', '--scratch-path', '.build/schema-examples-strict', '-Xswiftc', '-strict-memory-safety', '-Xswiftc', '-warnings-as-errors'], + ] + if lane == 'schema-examples': + return [ + ['python3', '-B', 'Scripts/public_check.py'], + ['swift', 'package', 'dump-package'], + ['swift', 'package', 'show-dependencies', '--format', 'json'], + ['swift', 'test', '--jobs', '2', '--test-product', 'JSONSchemaExamplesTests', '--filter', 'JSONSchemaExamplesTests'], + ['swift', 'test', '--jobs', '2', '--test-product', 'JSONSchemaTests'], + ['python3', '-B', 'Scripts/qualify_schema_examples.py', '--output', str(output / 'consumer'), '--scratch-path', '.build'], + ] if lane == 'composition': result = [ ['swift', 'test', '--filter', 'composition|composed|caughtOperationalFailureCannotComplete'], @@ -127,10 +157,16 @@ def commands(lane, output): ['python3', '-B', 'Scripts/qualify_release_1_1.py', '--mode', 'ownership', '--output', str(output / 'ownership.json')], ['python3', '-B', 'Scripts/qualify_release_1_1.py', '--mode', 'reader', '--output', str(output / 'reader.json')], ['swift', 'run', '-c', 'release', '--package-path', 'Examples/Consumer', 'Consumer'], + ['python3', '-B', 'Scripts/qualify_schema_examples.py', '--output', str(output / 'schema-examples'), '--scratch-path', '.build'], + ['python3', '-B', 'Scripts/qualify_schema_example_ownership.py', '--output', str(output / 'schema-example-ownership')], + ['python3', '-B', 'Scripts/qualify_schema_example_generation.py', '--output', str(output / 'schema-example-generation')], + ['python3', '-B', 'Scripts/qualify_schema_example_advanced.py', '--output', str(output / 'schema-example-advanced')], + ['python3', '-B', 'Scripts/qualify_schema_example_matrix.py', '--output', str(output / 'schema-example-matrix')], ['python3', '-B', 'Scripts/qualify_distribution.py', '--output', str(output / 'distribution')], ['python3', '-B', 'Scripts/qualify_ergonomics.py'], ['python3', '-B', 'Scripts/qualify_navigation.py', '--output', str(output / 'navigation')], ['python3', '-B', 'Scripts/qualify_inspection.py', '--output', str(output / 'inspection-consumer')], + ['python3', '-B', 'Scripts/qualify_snapshot.py', '--output', str(output / 'snapshot-consumer')], ['python3', '-B', 'Scripts/qualify_navigation_reducer.py', '--output', str(output / 'owned-codegen')], ['swift', 'test', '--package-path', 'Tools/MobileQualification'], ['swift', 'run', '-c', 'release', '--package-path', 'Tools/MobileQualification', 'MobileQualification'], @@ -144,6 +180,7 @@ def commands(lane, output): ['swift', 'test', '-c', 'release', '--sanitize', 'address', '--scratch-path', '.build/ci-address'], ['swift', 'test', '-c', 'release', '--sanitize', 'thread', '--scratch-path', '.build/ci-thread', '--filter', SHARED_TESTS], ['swift', 'build', '--scratch-path', '.build/ci-strict', '-Xswiftc', '-strict-memory-safety', '-Xswiftc', '-warnings-as-errors'], + ['python3', '-B', 'Scripts/qualify_schema_example_lifetime.py', '--output', str(output / 'corpus-lifetime')], ] if lane == 'r0': return [ @@ -179,7 +216,12 @@ def check_result(command, code, log, events=None): passed = code == 0 if events is not None: counts = events['bundle_counts'] if command[:2] == ['swift', 'test']: - expected = [DISCOVERY['composition_external_functions']] if 'Tools/CompositionQualification' in command else [12] if 'composition|composed|caughtOperationalFailureCannotComplete' in command else [88] if 'cli|sharedFilterPlan' in command else sorted(DISCOVERY['export_functions'].values()) if 'export|Export|apple' in command else [14] if 'Tools/WasmAudit' in command else sorted(DISCOVERY['qa_functions'].values()) if 'qa' in command else [7] if R0_TESTS in command else [3] if 'Tools/MCPReadiness' in command or 'Tools/MobileQualification' in command else [1] if 'Tools/Qualification' in command else sorted(SHARED_COUNTS.values()) if 'thread' in command else sorted(ROOT_COUNTS.values()) + expected = [DISCOVERY['composition_external_functions']] if 'Tools/CompositionQualification' in command else [12] if 'composition|composed|caughtOperationalFailureCannotComplete' in command else [79] if 'JSONSchemaTests' in command else [4 if 'thread' in command else 98] if 'JSONSchemaExamplesTests' in command else [88] if 'cli|sharedFilterPlan' in command else sorted(DISCOVERY['export_functions'].values()) if 'export|Export|apple' in command else [14] if 'Tools/WasmAudit' in command else sorted(DISCOVERY['qa_functions'].values()) if 'qa' in command else [7] if R0_TESTS in command else [3] if 'Tools/MCPReadiness' in command or 'Tools/MobileQualification' in command else [1] if 'Tools/Qualification' in command else sorted(SHARED_COUNTS.values()) if 'thread' in command else sorted(ROOT_COUNTS.values()) + if 'JSONSchemaExamplesTests' in command and events is not None: + passed = passed and events['module_function_counts'] == {'JSONSchemaExamplesTests': expected[0]} and events['parameter_case_invocations'] == 0 + if 'JSONSchemaTests' in command and events is not None: + parameters = {name: ROOT_PARAMETER_CASES[name] for name in ['successfulAlternativesCannotSwallowRegexExhaustion(keyword:)', 'URIIdentityUsesSyntaxNormalizationWithoutUnicodeFolding(input:expected:)']} + passed = passed and events['module_function_counts'] == {'JSONSchemaTests': 79} and events['parameterized_functions'] == parameters and events['parameter_case_invocations'] == 12 if 'navigation' in command: expected = [DISCOVERY['root_functions']['JSONNavigationTests']] if events is not None: @@ -193,9 +235,9 @@ def check_result(command, code, log, events=None): passed = passed and events['module_function_counts'] == {'CompositionScaleTests': DISCOVERY['composition_external_functions']} and events['parameter_case_invocations'] == 0 if 'qa' in command and events is not None: passed = passed and events['module_function_counts'] == DISCOVERY['qa_functions'] and check_restored(events) - if 'thread' in command and events is not None: + if 'thread' in command and 'JSONSchemaExamplesTests' not in command and events is not None: passed = passed and events['module_function_counts'] == SHARED_COUNTS - if '--filter' not in command and not any(x.startswith('Tools/') for x in command): + if '--filter' not in command and '--test-product' not in command and not any(x.startswith('Tools/') for x in command): if events is not None: passed = passed and events['module_function_counts'] == ROOT_COUNTS and check_restored(events) passed = passed and (events['parameterized_functions'] == ROOT_PARAMETER_CASES if events is not None else sum(int(n) for n in re.findall(r'with (\d+) test cases passed', log)) == PARAMETER_INVOCATIONS) @@ -225,10 +267,25 @@ def check_result(command, code, log, events=None): passed = data['observed'] == data['passed'] == expected and data['failed'] == data['policy'] == data['blocked'] == 0 if command == ['swift', 'package', 'dump-package'] and passed: d = json.loads(log) - passed = sorted(p['name'] for p in d['products']) == ['JSON', 'JSONBridges', 'JSONHTML', 'JSONInspection', 'JSONNavigation', 'JSONPatch', 'JSONSchema', 'JSONStreaming', 'swift-json'] and not d['dependencies'] and d['swiftLanguageVersions'] == ['6'] and d['toolsVersion']['_version'] == '6.4.0' and d['platforms'] == [{'options': [], 'platformName': 'macos', 'version': '27.0'}, {'options': [], 'platformName': 'ios', 'version': '27.0'}] - passed = passed and {p['name']: p['targets'] for p in d['products']} == dict({name: [name] for name in ['JSON', 'JSONSchema', 'JSONStreaming', 'JSONPatch', 'JSONHTML', 'JSONBridges', 'JSONNavigation', 'JSONInspection']}, **{'swift-json': ['JSONCLI']}) + passed = sorted(p['name'] for p in d['products']) == ['JSON', 'JSONBridges', 'JSONHTML', 'JSONInspection', 'JSONNavigation', 'JSONPatch', 'JSONSchema', 'JSONSchemaExamples', 'JSONStreaming', 'swift-json'] and not d['dependencies'] and d['swiftLanguageVersions'] == ['6'] and d['toolsVersion']['_version'] == '6.4.0' and d['platforms'] == [{'options': [], 'platformName': 'macos', 'version': '27.0'}, {'options': [], 'platformName': 'ios', 'version': '27.0'}] + passed = passed and {p['name']: p['targets'] for p in d['products']} == dict({name: [name] for name in ['JSON', 'JSONSchema', 'JSONSchemaExamples', 'JSONStreaming', 'JSONPatch', 'JSONHTML', 'JSONBridges', 'JSONNavigation', 'JSONInspection']}, **{'swift-json': ['JSONCLI']}) targets = {t['name']: t for t in d['targets']} - passed = passed and set(targets) == {'JSON', 'JSONNavigation', 'JSONNavigationTests', 'JSONInspection', 'JSONInspectionTests', 'JSONIntegrationTests', 'JSONBridges', 'JSONBridgesTests', 'JSONSchema', 'JSONStreaming', 'JSONPatch', 'JSONHTML', 'JSONRegexRuntime', 'JSONTests', 'JSONSchemaTests', 'JSONStreamingTests', 'JSONPatchTests', 'JSONHTMLTests', 'JSONRegexRuntimeTests', 'JSONFilterRuntime', 'JSONCLIEngine', 'JSONCLI', 'JSONCLITests'} and targets['JSONHTML']['dependencies'] == [{'byName': ['JSON', None]}] and targets['JSONBridges']['dependencies'] == [{'byName': ['JSON', None]}] and targets['JSONNavigation']['dependencies'] == [{'byName': ['JSON', None]}] and targets['JSONNavigationTests']['dependencies'] == [{'byName': ['JSONNavigation', None]}, {'byName': ['JSON', None]}] and targets['JSONInspection']['dependencies'] == [{'byName': ['JSON', None]}, {'byName': ['JSONNavigation', None]}] and targets['JSONInspectionTests']['dependencies'] == [{'byName': ['JSONInspection', None]}, {'byName': ['JSONNavigation', None]}, {'byName': ['JSON', None]}] and targets['JSONFilterRuntime']['dependencies'] == [{'byName': ['JSON', None]}] and targets['JSONCLI']['type'] == 'executable' + passed = passed and set(targets) == {'JSON', 'JSONNavigation', 'JSONNavigationTests', 'JSONInspection', 'JSONInspectionTests', 'JSONIntegrationTests', 'JSONBridges', 'JSONBridgesTests', 'JSONSchema', 'JSONSchemaExamples', 'JSONSchemaExamplesTests', 'JSONStreaming', 'JSONPatch', 'JSONHTML', 'JSONRegexRuntime', 'JSONTests', 'JSONSchemaTests', 'JSONStreamingTests', 'JSONPatchTests', 'JSONHTMLTests', 'JSONRegexRuntimeTests', 'JSONFilterRuntime', 'JSONCLIEngine', 'JSONCLI', 'JSONCLITests'} and targets['JSONHTML']['dependencies'] == [{'byName': ['JSON', None]}] and targets['JSONBridges']['dependencies'] == [{'byName': ['JSON', None]}] and targets['JSONFilterRuntime']['dependencies'] == [{'byName': ['JSON', None]}] and targets['JSONCLI']['type'] == 'executable' + passed = passed and targets['JSONNavigation']['dependencies'] == [{'byName': ['JSON', None]}] and targets['JSONNavigationTests']['dependencies'] == [{'byName': ['JSONNavigation', None]}, {'byName': ['JSON', None]}] + passed = passed and targets['JSONSchemaExamples']['dependencies'] == [{'byName': ['JSON', None]}, {'byName': ['JSONSchema', None]}] and targets['JSON']['dependencies'] == [] and targets['JSONSchema']['dependencies'] == [{'byName': ['JSON', None]}, {'byName': ['JSONRegexRuntime', None]}] + passed = passed and targets['JSONInspection']['dependencies'] == [{'byName': ['JSON', None]}, {'byName': ['JSONNavigation', None]}] and targets['JSONInspectionTests']['dependencies'] == [{'byName': ['JSONInspection', None]}, {'byName': ['JSONNavigation', None]}, {'byName': ['JSON', None]}] + if command[:3] == ['python3', '-B', 'Scripts/qualify_schema_example_matrix.py']: + passed = passed and 'SCHEMA_EXAMPLES_MATRIX passed schemas=64 certified=512 invalid=64 processes=2' in log.splitlines() + if command[:3] == ['python3', '-B', 'Scripts/qualify_schema_example_lifetime.py']: + passed = passed and 'SCHEMA_EXAMPLES_CORPUS passed mode=owned scopes=128 values=6 exact=1' in log.splitlines() + if command[:3] == ['python3', '-B', 'Scripts/qualify_schema_example_advanced.py']: + passed = passed and 'SCHEMA_EXAMPLES_ADVANCED passed required=10 incomplete=8 profiles=2 seeds=4 per_process=144 processes=2 witnesses=155' in log.splitlines() + if command[:3] == ['python3', '-B', 'Scripts/qualify_schema_example_generation.py']: + passed = passed and 'SCHEMA_EXAMPLES_GENERATION passed cases=24 profiles=2 seeds=4 per_process=192 processes=2 proofs=2' in log.splitlines() + if command[:3] == ['python3', '-B', 'Scripts/qualify_schema_example_ownership.py']: + passed = passed and 'SCHEMA_EXAMPLES_OWNERSHIP passed positive=1 negatives=8' in log.splitlines() + if command[:3] == ['python3', '-B', 'Scripts/qualify_schema_examples.py']: + passed = passed and 'SCHEMA_EXAMPLES_COMPILER passed public_cases=15 negatives=2' in log.splitlines() and 'SCHEMA_EXAMPLES_DOCUMENTATION_COMPILER passed snippets=10 processes=2' in log.splitlines() if command == ['swift', 'package', 'show-dependencies', '--format', 'json'] and passed: passed = json.loads(log)['dependencies'] == [] if command[:2] == ['swift', 'run'] and 'CompositionConsumer' in command: @@ -243,6 +300,8 @@ def check_result(command, code, log, events=None): passed = passed and 'ERGONOMICS_COMPILER passed commands=8 guide_fences=3' in log.splitlines() if command[:3] == ['python3', '-B', 'Scripts/qualify_navigation_reducer.py']: passed = passed and 'REDUCER_CODEGEN passed positive=2 negative=20' in log.splitlines() + if command[:3] == ['python3', '-B', 'Scripts/qualify_snapshot.py']: + passed = passed and 'SNAPSHOT_COMPILER passed configurations=2 consumer_cases=14 address_cases=14 guide_fences=3 negative_probes=18' in log.splitlines() if command[:3] == ['python3', '-B', 'Scripts/qualify_inspection.py']: passed = passed and 'INSPECTION_COMPILER passed configurations=2 consumer_cases=12 address_cases=12 guide_fences=3 negative_probes=8' in log.splitlines() if command[:3] == ['python3', '-B', 'Scripts/qualify_navigation.py']: @@ -259,7 +318,7 @@ def check_result(command, code, log, events=None): def main(): parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('--lane', choices=['composition', 'native', 'safety', 'docs', 'r0', 'qa', 'linux', 'linux-safety', 'wasm', 'browser', 'export', 'cli', 'coverage', 'linux-coverage', 'linux-qa', 'wasm-qa', 'archive', 'navigation', 'inspection'], required=True) + parser.add_argument('--lane', choices=['composition', 'navigation', 'inspection', 'native', 'safety', 'docs', 'r0', 'qa', 'linux', 'linux-safety', 'wasm', 'browser', 'export', 'cli', 'schema-examples', 'schema-examples-safety', 'schema-examples-determinism', 'schema-examples-construction', 'schema-examples-advanced', 'schema-examples-qualification', 'coverage', 'linux-coverage', 'linux-qa', 'wasm-qa', 'archive'], required=True) parser.add_argument('--output') args = parser.parse_args() output = ROOT / (args.output or '.build/ci/' + args.lane) diff --git a/Scripts/coverage.py b/Scripts/coverage.py index 13190a1..fefa0a6 100644 --- a/Scripts/coverage.py +++ b/Scripts/coverage.py @@ -46,7 +46,7 @@ def shared_objects(bin_directory): def without_schema_objects(objects): # Integration imports schema too, including the private regex implementation. - omitted = {'JSONSchemaTests/runner', 'JSONSchemaTests', 'JSONSchema', + omitted = {'JSONSchemaExamplesTests/runner', 'JSONSchemaExamplesTests', 'JSONSchemaExamples', 'JSONSchemaTests/runner', 'JSONSchemaTests', 'JSONSchema', 'JSONIntegrationTests/runner', 'JSONIntegrationTests', 'JSONCLITests/runner', 'JSONCLITests', 'JSONCLIEngine', 'swift-json/runner'} return {name:path for name,path in objects.items() if name not in omitted} diff --git a/Scripts/documentation_catalog.py b/Scripts/documentation_catalog.py index 5a0cdd8..06c5267 100644 --- a/Scripts/documentation_catalog.py +++ b/Scripts/documentation_catalog.py @@ -7,7 +7,7 @@ import urllib.parse ROOT = pathlib.Path(__file__).resolve().parents[1] -PRODUCTS = ('JSON', 'JSONSchema', 'JSONStreaming', 'JSONPatch', 'JSONHTML', 'JSONBridges', 'JSONNavigation', 'JSONInspection') +PRODUCTS = ('JSON', 'JSONSchema', 'JSONSchemaExamples', 'JSONStreaming', 'JSONPatch', 'JSONHTML', 'JSONBridges', 'JSONNavigation', 'JSONInspection') def public_symbols(graph_dir): diff --git a/Scripts/qualify_schema_example_advanced.py b/Scripts/qualify_schema_example_advanced.py new file mode 100644 index 0000000..983c12e --- /dev/null +++ b/Scripts/qualify_schema_example_advanced.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +"""Materialize ten G4 successes and eight honest incomplete-search cases through public APIs.""" +import argparse, hashlib, json, pathlib, subprocess, sys +from decimal import Decimal + +ROOT = pathlib.Path(__file__).resolve().parents[1] +ORIGINAL = '6f2bfd4fdb7c7e206b6570c2156ca8e248831840733b0258aa8cd5938b35649f' +AMENDED = '5b3e6e40e83439a810d0eb7ac412ce158a4e582f36b9d36d6e313c159654ddd3' + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--output', required=True) + parser.add_argument('--scratch-path', default='.build') + args = parser.parse_args() + output = ROOT / args.output + output.mkdir(parents=True, exist_ok=True) + rows = [] + receipt = {'schema_version': 1, 'status': 'failed', 'commands': rows, + 'scope': 'G4 public native construction and honest incompleteness; all 155 native witness checks, not new external-oracle or G7 platform qualification'} + + def run(name, command): + result = subprocess.run(command, cwd=ROOT, capture_output=True, text=True, timeout=1200) + text = result.stdout + result.stderr + log = output / (name + '.log') + log.write_text(text.replace(str(ROOT), '.')) + rows.append({'command': command, 'exit_code': result.returncode, 'log': log.relative_to(ROOT).as_posix(), + 'log_sha256': hashlib.sha256(log.read_bytes()).hexdigest()}) + if result.returncode: raise ValueError('Failed actual public consumer: ' + name) + return text + + try: + fixtures = ROOT / 'Tests/Fixtures/SchemaExamples' + fixture = fixtures / 'S3-integral-correction-v1.json' + manifest_path = fixtures / 'Manifest.json' + manifest = json.loads(manifest_path.read_text()) + assert hashlib.sha256(fixture.read_bytes()).hexdigest() == manifest['amended_sha256'] == AMENDED + assert hashlib.sha256((fixtures / 'S3-original-v1.json').read_bytes()).hexdigest() == manifest['original_sha256'] == ORIGINAL + required_ids = manifest['g4_must_generate_ids'] + incomplete_ids = manifest['g4_may_fail_ids'] + assert len(required_ids) == len(set(required_ids)) == 10 + assert len(incomplete_ids) == len(set(incomplete_ids)) == 8 + assert not set(required_ids) & set(incomplete_ids) + location = run('bin-path', ['swift', 'build', '--package-path', 'Examples/SchemaExamplesConsumer', '--show-bin-path', '--scratch-path', args.scratch_path]) + bins = [pathlib.Path(line) for line in location.splitlines() if line.startswith('/')] + assert len(bins) == 1 + binary = bins[0] / 'SchemaExamplesConsumer' + receipts = [] + for index in range(2): + text = run('generation-process-' + str(index), [str(binary), '--g4-fixtures', str(fixture), '--g4-manifest', str(manifest_path)]) + assert 'SCHEMA_EXAMPLES_G4 passed required=10 incomplete=8 profiles=2 seeds=4 invocations=144 witnesses=155' in text.splitlines() + report_lines = [line.removeprefix('G4_RECEIPT ') for line in text.splitlines() if line.startswith('G4_RECEIPT ')] + assert len(report_lines) == 1 + report = json.loads(report_lines[0], parse_float=Decimal) + assert report['requiredIDs'] == required_ids and report['incompleteIDs'] == incomplete_ids + assert report['schemas'] == 44 and report['witnesses'] == 155 and report['mustValid'] == 80 + assert report['algorithm'] == 's3-candidates-v4' and len(report['results']) == 144 + expected = {(name, profile, seed) for name in required_ids + incomplete_ids for profile in ['minimal','demonstration'] for seed in [0,1,42,2**64-1]} + observed = {(r['id'],r['profile'],r['seed']) for r in report['results']} + assert observed == expected + must_valid = may_valid = explicit = 0 + for r in report['results']: + required = r['id'] in required_ids + assert r['required'] is required + if r['outcome'] == 'validated': + assert r['nativeCertified'] is r['independentNativeValid'] is r['roundtrip'] is True + assert 0 < r['evaluationWork'] <= r['work'] and r['remainingWork'] >= 0 + assert 1 <= r['attempts'] <= 16 and len(r['trace']) == len(r['snapshot']) == len(r['corpus']) == len(r['policy']) == 64 + assert 'value' in r + if required: must_valid += 1 + else: may_valid += 1 + else: + assert not required and r['outcome'] in ['unsupported','searchExhausted'] + assert not any(k in r for k in ['value','work','evaluationWork','remainingWork','nativeCertified']) + if r['outcome'] == 'unsupported': assert r['reason'] in ['candidateConstruction','dynamicReference','cyclicReference'] + else: assert 1 <= r['attempts'] <= 16 + explicit += 1 + assert must_valid == 80 and may_valid + explicit == 64 + assert may_valid == report['mayValid'] and explicit == report['explicit'] + (output / ('generation-process-' + str(index) + '.json')).write_text(report_lines[0] + '\n') + receipts.append(report) + assert receipts[0] == receipts[1], 'Cross-process generation/trace/accounting drift' + receipt.update(status='passed', required_cases=10, incomplete_cases=8, profiles=2, seeds=4, + invocations_per_process=144, required_valid_per_process=80, may_valid_per_process=may_valid, + explicit_incomplete_per_process=explicit, processes=2, fixture_schemas=44, native_witnesses_per_process=155, + input_sha256=AMENDED, original_sha256=ORIGINAL, + binary_sha256=hashlib.sha256(binary.read_bytes()).hexdigest(), + required_case_ids=required_ids, incomplete_case_ids=incomplete_ids, + classification='A valid witness is success; unsupported/exhausted is incomplete search; native operational errors fail acceptance') + except (AssertionError, ValueError, OSError, subprocess.SubprocessError) as error: + receipt['error'] = str(error) or type(error).__name__ + (output / 'receipt.json').write_text(json.dumps(receipt, indent=2) + '\n') + print('SCHEMA_EXAMPLES_ADVANCED ' + receipt['status'] + ' required=10 incomplete=8 profiles=2 seeds=4 per_process=144 processes=2 witnesses=155') + return 0 if receipt['status'] == 'passed' else 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/Scripts/qualify_schema_example_generation.py b/Scripts/qualify_schema_example_generation.py new file mode 100644 index 0000000..bac1e39 --- /dev/null +++ b/Scripts/qualify_schema_example_generation.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +"""Materialize the 24-case G3 acceptance matrix through actual public APIs.""" +import argparse, hashlib, json, pathlib, subprocess, sys +from decimal import Decimal + +ROOT = pathlib.Path(__file__).resolve().parents[1] +ORIGINAL = '6f2bfd4fdb7c7e206b6570c2156ca8e248831840733b0258aa8cd5938b35649f' +AMENDED = '5b3e6e40e83439a810d0eb7ac412ce158a4e582f36b9d36d6e313c159654ddd3' + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--output', required=True) + parser.add_argument('--scratch-path', default='.build') + args = parser.parse_args() + output = ROOT / args.output + output.mkdir(parents=True, exist_ok=True) + rows = [] + receipt = {'schema_version': 1, 'status': 'failed', 'commands': rows, + 'scope': 'Public native generation/validation/roundtrip; not an independent external validator or G4/G7 qualification'} + + def run(name, command): + result = subprocess.run(command, cwd=ROOT, capture_output=True, text=True, timeout=1200) + text = result.stdout + result.stderr + log = output / (name + '.log') + log.write_text(text.replace(str(ROOT), '.')) + rows.append({'command': command, 'exit_code': result.returncode, 'log': log.relative_to(ROOT).as_posix(), + 'log_sha256': hashlib.sha256(log.read_bytes()).hexdigest()}) + if result.returncode: raise ValueError('Failed actual public consumer: ' + name) + return text + + try: + fixtures = ROOT / 'Tests/Fixtures/SchemaExamples' + fixture = fixtures / 'S3-integral-correction-v1.json' + manifest_path = fixtures / 'Manifest.json' + manifest = json.loads(manifest_path.read_text()) + assert hashlib.sha256(fixture.read_bytes()).hexdigest() == manifest['amended_sha256'] == AMENDED + assert hashlib.sha256((fixtures / 'S3-original-v1.json').read_bytes()).hexdigest() == manifest['original_sha256'] == ORIGINAL + expected_ids = manifest['g3_must_generate_ids'] + assert len(expected_ids) == len(set(expected_ids)) == 24 + location = run('bin-path', ['swift', 'build', '--package-path', 'Examples/SchemaExamplesConsumer', '--show-bin-path', '--scratch-path', args.scratch_path]) + bins = [pathlib.Path(line) for line in location.splitlines() if line.startswith('/')] + assert len(bins) == 1 + binary = bins[0] / 'SchemaExamplesConsumer' + receipts = [] + for index in range(2): + text = run('generation-process-' + str(index), [str(binary), '--g3-fixtures', str(fixture), '--g3-manifest', str(manifest_path)]) + assert 'SCHEMA_EXAMPLES_G3 passed cases=24 profiles=2 seeds=4 validated=192 witnesses=90 proofs=2' in text.splitlines() + report_lines = [line.removeprefix('G3_RECEIPT ') for line in text.splitlines() if line.startswith('G3_RECEIPT ')] + assert len(report_lines) == 1 + report = json.loads(report_lines[0], parse_float=Decimal) + assert report['ids'] == expected_ids and report['witnesses'] == 90 + assert report['proofs'] == manifest['g3_sound_proof_ids'] + assert report['algorithm'] == 's3-candidates-v4' and len(report['results']) == 192 + expected = {(name, profile, seed) for name in expected_ids for profile in ['minimal','demonstration'] for seed in [0,1,42,2**64-1]} + observed = {(r['id'],r['profile'],r['seed']) for r in report['results']} + assert observed == expected + for r in report['results']: + assert r['nativeCertified'] is r['independentNativeValid'] is r['roundtrip'] is True + assert 0 < r['evaluationWork'] <= r['work'] and r['remainingWork'] >= 0 + assert r['attempts'] >= 1 and len(r['trace']) == len(r['snapshot']) == len(r['corpus']) == len(r['policy']) == 64 + (output / ('generation-process-' + str(index) + '.json')).write_text(report_lines[0] + '\n') + receipts.append(report) + assert receipts[0] == receipts[1], 'Cross-process generation/trace/accounting drift' + receipt.update(status='passed', cases=24, profiles=2, seeds=4, generated_per_process=192, + processes=2, witnesses_per_process=90, sound_proofs_per_process=2, + input_sha256=AMENDED, original_sha256=ORIGINAL, + binary_sha256=hashlib.sha256(binary.read_bytes()).hexdigest(), + expected_case_ids=expected_ids, source_policies_excluded_from_successes=['18 G4 cases']) + except (AssertionError, ValueError, OSError, subprocess.SubprocessError) as error: + receipt['error'] = str(error) or type(error).__name__ + (output / 'receipt.json').write_text(json.dumps(receipt, indent=2) + '\n') + print('SCHEMA_EXAMPLES_GENERATION ' + receipt['status'] + ' cases=24 profiles=2 seeds=4 per_process=192 processes=2 proofs=2') + return 0 if receipt['status'] == 'passed' else 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/Scripts/qualify_schema_example_lifetime.py b/Scripts/qualify_schema_example_lifetime.py new file mode 100644 index 0000000..8a16eda --- /dev/null +++ b/Scripts/qualify_schema_example_lifetime.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +"""ASan public-consumer process lifetime: no suppressions or ignored leak exits.""" +import argparse, hashlib, json, os, pathlib, subprocess, sys +ROOT = pathlib.Path(__file__).resolve().parents[1] +SOURCE = r'''import JSON +import JSONSchemaExamples + +@inline(never) func checkCorpus() throws { + let corpus = JSONSchemaExampleCorpus.synthetic + let text = try JSONWriter().writeString(.array(corpus.values)) + precondition(text == #"[null,false,0,"example",[],{}]"#) + var changed = corpus.values + changed.append(.string("local")) + precondition(changed.count == 7 && corpus.values.count == 6) + precondition(JSONSchemaExampleOptions().corpus.values.count == 6) +} + +@main struct CorpusLifetime { + static func main() throws { + for _ in 0..<128 { try checkCorpus() } + print("SCHEMA_EXAMPLES_CORPUS passed mode=owned scopes=128 values=6 exact=1") + } +} +''' +MARKER = 'SCHEMA_EXAMPLES_CORPUS passed mode=owned scopes=128 values=6 exact=1' + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--output', required=True) + args = parser.parse_args() + out = ROOT / args.output; out.mkdir(parents=True, exist_ok=True) + package = out / 'consumer'; sources = package / 'Sources/CorpusLifetime'; sources.mkdir(parents=True, exist_ok=True) + (package / 'Package.swift').write_text('''// swift-tools-version: 6.4 +import PackageDescription +let package = Package(name: "CorpusLifetime", platforms: [.macOS("27.0"), .iOS("27.0")], + dependencies: [.package(name: "Swift-JSON", path: ''' + json.dumps(str(ROOT)) + ''')], + targets: [.executableTarget(name: "CorpusLifetime", dependencies: [ + .product(name: "JSON", package: "Swift-JSON"), + .product(name: "JSONSchemaExamples", package: "Swift-JSON")])], swiftLanguageModes: [.v6]) +''') + (sources / 'CorpusLifetime.swift').write_text(SOURCE) + report = dict(status='failed', commands=[], scopes=128, values=6, sanitizer_suppressions=False, + scope='Real public products, repeated exact synthetic corpus/options reads and caller copy mutation; process exit must be ASan/LSan clean.') + def run(name, command, timeout, environment=None): + log = out / (name + '.log') + result = subprocess.run(command, cwd=ROOT, capture_output=True, text=True, timeout=timeout, env=environment) + log.write_text(result.stdout + result.stderr) + report['commands'].append(dict(command=command, exit_code=result.returncode, log=str(log.relative_to(ROOT)), log_sha256=hashlib.sha256(log.read_bytes()).hexdigest())) + if result.returncode: raise ValueError(name + ' exited ' + str(result.returncode)) + return log.read_text() + try: + build = ['swift', 'build', '-c', 'release', '--sanitize', 'address', '--jobs', '2', '--package-path', str(package), '--scratch-path', '.build/schema-example-lifetime'] + run('build', build, 1200) + path = run('bin-path', build + ['--show-bin-path'], 120) + binary = pathlib.Path(path.strip()) / 'CorpusLifetime' + environment = None + if sys.platform.startswith('linux'): + environment = dict(os.environ, ASAN_OPTIONS='detect_leaks=1', LSAN_OPTIONS='') + report['process_environment_overrides'] = {'ASAN_OPTIONS': 'detect_leaks=1', 'LSAN_OPTIONS': ''} + text = run('process', [str(binary)], 120, environment) + if text.splitlines() != [MARKER]: raise ValueError('Missing or additional process outcome') + report.update(status='passed', binary_sha256=hashlib.sha256(binary.read_bytes()).hexdigest()) + except (OSError, ValueError, subprocess.SubprocessError) as error: + report['error'] = str(error) + (out / 'receipt.json').write_text(json.dumps(report, indent=2) + '\n') + print(MARKER if report['status'] == 'passed' else 'SCHEMA_EXAMPLES_CORPUS failed') + return 0 if report['status'] == 'passed' else 1 + +if __name__ == '__main__': raise SystemExit(main()) diff --git a/Scripts/qualify_schema_example_matrix.py b/Scripts/qualify_schema_example_matrix.py new file mode 100644 index 0000000..0066d2a --- /dev/null +++ b/Scripts/qualify_schema_example_matrix.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +"""Public bounded grammar replay, exact identities and two-process reproducibility.""" +import argparse,hashlib,json,pathlib,subprocess,sys +from decimal import Decimal +ROOT=pathlib.Path(__file__).resolve().parents[1] +FIXTURE_SHA='cb55604183a6db7df5fa968db283e7abeae794277d2b0cef033b0bdb92e615cb' + +def main(): + p=argparse.ArgumentParser(description=__doc__);p.add_argument('--output',required=True);p.add_argument('--scratch-path',default='.build');a=p.parse_args() + out=ROOT/a.output;out.mkdir(parents=True,exist_ok=True) + receipt=dict(status='failed',commands=[],scope='64 bounded grammar cases, not exhaustive arbitrary schemas; independent external oracle is a separate research receipt') + def run(name,cmd): + r=subprocess.run(cmd,cwd=ROOT,capture_output=True,text=True,timeout=1500) + text=r.stdout+r.stderr;log=out/(name+'.log');log.write_text(text) + receipt['commands'].append(dict(command=cmd,exit_code=r.returncode,log=log.name,sha256=hashlib.sha256(log.read_bytes()).hexdigest())) + if r.returncode:raise ValueError(name+' failed') + return text + try: + fixture=ROOT/'Tests/Fixtures/SchemaExamples/S3-G7-seeded-v1.json' + assert hashlib.sha256(fixture.read_bytes()).hexdigest()==FIXTURE_SHA + cases=json.loads(fixture.read_text())['cases'];ids={r['id'] for r in cases};assert len(ids)==len(cases)==64 + location=run('bin-path',['swift','build','--package-path','Examples/SchemaExamplesConsumer','--show-bin-path','--scratch-path',a.scratch_path]) + binary=pathlib.Path(location.strip())/'SchemaExamplesConsumer' + outputs=[] + expected={(i,p,s) for i in ids for p in ['minimal','demonstration'] for s in [0,1,42,2**64-1]} + for i in range(2): + text=run('process-'+str(i),[str(binary),'--g7-fuzz',str(fixture)]) + assert 'SCHEMA_EXAMPLES_G7 passed schemas=64 certified=512 invalid=64' in text.splitlines() + lines=[l.removeprefix('G7_RESULT ') for l in text.splitlines() if l.startswith('G7_RESULT ')] + rows=[json.loads(l,parse_float=Decimal) for l in lines] + assert len(rows)==512 and {(r['id'],r['profile'],r['seed']) for r in rows}==expected + assert all(0 0 and any(expected in d for d in diagnostics)) + log = output / (name + '.log') + log.write_text(text.replace(str(ROOT), '.')) + receipt['commands'].append(dict(name=name, command=[x.replace(str(ROOT), '.') for x in command], + exit_code=result.returncode, status='passed' if passed else 'failed', compiler_crash=crash, + expected_diagnostic=expected, primary_diagnostics=diagnostics, + log=log.relative_to(ROOT).as_posix(), log_sha256=hashlib.sha256(log.read_bytes()).hexdigest())) + save() + if not passed: + raise ValueError('Unexpected compiler/consumer outcome: ' + name) + return result.stdout + + def consumer(name, command): + text = run(name, command) + checks = re.findall(r'^SNAPSHOT_CHECK (\S+) passed$', text, re.M) + if len(checks) != 14 or set(checks) != CHECKS or 'SNAPSHOT_CONSUMER passed cases=14' not in text: + raise ValueError('Missing/duplicate inspection consumer checks: ' + name) + resource = re.search(r'^SNAPSHOT_RESOURCE work=(\d+) retained=(\d+) peak=(\d+) nodes=(\d+)$', text, re.M) + if resource is None or not all(int(x) > 0 for x in resource.groups()): + raise ValueError('Missing finite cumulative report: ' + name) + receipt['consumer_results'][name] = dict(cases=checks, work_units=int(resource[1]), + logical_retained_bytes=int(resource[2]), peak_logical_retained_bytes=int(resource[3]), + admitted_nodes=int(resource[4])) + save() + + try: + for config in ['debug', 'release']: + run(config + '-build', ['swift', 'build', '-c', config, '--target', 'JSONInspection']) + binpath = pathlib.Path(run(config + '-binpath', ['swift', 'build', '-c', config, '--show-bin-path']).strip()) + modules = binpath / 'Modules' if (binpath / 'Modules').is_dir() else binpath + objects = [] + for module in ['JSON', 'JSONNavigation', 'JSONInspection']: + aggregate = binpath / (module + '.o') + selected = [aggregate] if aggregate.is_file() else sorted((binpath / (module + '.build')).glob('*.o')) + if not selected: + raise ValueError('Missing built objects: ' + module) + objects.extend(str(p) for p in selected) + common = ['swiftc', '-swift-version', '6', '-warnings-as-errors', '-package-name', + 'snapshot_external_consumer', '-I', str(modules), + '-O' if config == 'release' else '-Onone'] + consumer(config, ['swift', 'run', '-c', config, '--package-path', + 'Tools/SnapshotQualification', 'SnapshotConsumer']) + guide = ROOT / 'Sources/JSONInspection/Documentation.docc/StructuralSnapshots.md' + fences = re.findall(r'```swift\n(.*?)\n```', guide.read_text(), re.S) + if len(fences) != 3: + raise ValueError('Changed three-fence guide inventory') + for index, fence in enumerate(fences): + file = output / ('guide-' + str(index) + '.swift') + file.write_text('import Foundation\nimport JSON\nimport JSONNavigation\nimport JSONInspection\n' + '@main struct Guide { static func main() throws {\n' + fence + '\n} }\n') + binary = output / ('guide-' + config + '-' + str(index)) + run(config + '-guide-' + str(index), common + ['-parse-as-library', str(file), *objects, '-o', str(binary)]) + run(config + '-guide-' + str(index) + '-run', [str(binary)]) + probes = [ + ('core-import-isolation', 'import JSON\nlet x = JSONStructuralSnapshotBuilder.self\n', 'cannot find'), + ('scope-isolation', 'import JSON\nlet x = JSONReadScope.self\n', 'cannot find'), + ('meter-isolation', 'import JSON\nlet x = JSONWorkMeter.self\n', 'cannot find'), + ('capability-isolation', 'import JSONNavigation\nlet x = JSONTraversalContext.self\n', 'cannot find'), + ('reducer-isolation', 'import JSONNavigation\nlet x = JSONTraversalOwnedReducer.self\n', 'cannot find'), + ('record-is-readonly', 'import JSONInspection\nfunc change(_ x: inout JSONStructuralRecord) { x.subtreeEndExclusive = 7 }\n', 'setter is inaccessible'), + ('source-is-readonly', 'import JSON\nimport JSONInspection\nfunc change(_ x: inout JSONStructuralSnapshot) { x.document = .init(root: .null) }\n', "'let' constant"), + ('snapshot-is-unforgeable', 'import JSON\nimport JSONInspection\nlet x = JSONStructuralSnapshot(document: .init(root: .null), records: [])\n', 'initializer is inaccessible'), + ('no-source-rebinding', 'import JSON\nimport JSONInspection\nfunc change(_ x: JSONStructuralSnapshot) throws { _ = try x.value(for: 0, document: .init(root: .null)) }\n', "extra argument 'document'"), + ] + for name, source, expected in probes: + file = output / (name + '.swift'); file.write_text(source) + run(config + '-' + name, common + ['-parse-as-library', '-c', str(file), + '-o', str(output / (config + '-' + name + '.o'))], expected) + consumer('address', ['swift', 'run', '-c', 'release', '--sanitize', 'address', + '--scratch-path', '.build/snapshot-consumer-address', '--package-path', + 'Tools/SnapshotQualification', 'SnapshotConsumer']) + receipt['status'] = 'passed' + except (OSError, ValueError, subprocess.TimeoutExpired) as error: + receipt.update(status='failed', error=str(error)) + save() + print('SNAPSHOT_COMPILER ' + receipt['status'] + + ' configurations=2 consumer_cases=14 address_cases=14 guide_fences=3 negative_probes=18') + return 0 if receipt['status'] == 'passed' else 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/Scripts/schema_example_documentation.py b/Scripts/schema_example_documentation.py new file mode 100644 index 0000000..1dcb546 --- /dev/null +++ b/Scripts/schema_example_documentation.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +"""Synchronize every JSONSchemaExamples DocC Swift block with its public consumer.""" +import argparse +import hashlib +import json +import pathlib +import re + +ROOT = pathlib.Path(__file__).resolve().parents[1] +CATALOG = ROOT / 'Sources/JSONSchemaExamples/Documentation.docc' +CONSUMER = ROOT / 'Examples/SchemaExamplesConsumer/Sources/SchemaExamplesConsumer/DocumentationExamples.swift' +SNIPPETS = 10 +IMPORTS = {'import JSON', 'import JSONSchema', 'import JSONSchemaExamples'} + + +def examples(): + records = [] + for path in sorted(CATALOG.glob('*.md')): + text = path.read_text() + for index, match in enumerate(re.finditer(r'```swift\n(.*?)\n```', text, re.S), 1): + code = match.group(1) + if not code.strip(): + raise ValueError('Empty documentation snippet: ' + path.name) + imports = [line for line in code.splitlines() if line.startswith('import ')] + if not set(imports) <= IMPORTS: + raise ValueError('Unexpected documentation import: ' + path.name) + body = '\n'.join(line for line in code.splitlines() if not line.startswith('import ')).strip() + records.append({'path': path.relative_to(ROOT).as_posix(), 'block': index, + 'line': text[:match.start(1)].count('\n') + 1, + 'sha256': hashlib.sha256(code.encode()).hexdigest(), 'body': body}) + if len(records) != SNIPPETS: + raise ValueError(f'Expected exactly {SNIPPETS} guide snippets, found {len(records)}') + return records + + +def consumer_source(): + records = examples() + source = '// SPDX-License-Identifier: Apache-2.0\n// Generated from every JSONSchemaExamples DocC Swift block; do not edit by hand.\n' + source += '\n'.join(sorted(IMPORTS)) + '\n\nfunc runSchemaExampleDocumentation() throws -> Int {\n var completed = 0\n' + for record in records: + source += ' do {\n' + source += f' #sourceLocation(file: "{record["path"]}", line: {record["line"]})\n' + source += '\n'.join(' ' + line if line else '' for line in record['body'].splitlines()) + '\n' + source += ' #sourceLocation()\n completed += 1\n }\n' + source += f' precondition(completed == {SNIPPETS})\n return completed\n}}\n' + return source + + +def check(): + expected = consumer_source() + if CONSUMER.read_text() != expected: + raise ValueError('DocC Swift snippets differ from their compiled public consumer; run Scripts/schema_example_documentation.py --write and review') + records = [{k: v for k, v in row.items() if k != 'body'} for row in examples()] + return {'status': 'passed', 'snippets': SNIPPETS, 'guides': len({r['path'] for r in records}), + 'consumer': CONSUMER.relative_to(ROOT).as_posix(), + 'consumer_sha256': hashlib.sha256(expected.encode()).hexdigest(), 'sources': records} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--write', action='store_true') + args = parser.parse_args() + if args.write: + CONSUMER.write_text(consumer_source()) + print(json.dumps(check(), indent=2)) + + +if __name__ == '__main__': + main() diff --git a/Scripts/test_qualification_harness.py b/Scripts/test_qualification_harness.py index cbf9ad7..ea91700 100644 --- a/Scripts/test_qualification_harness.py +++ b/Scripts/test_qualification_harness.py @@ -32,6 +32,31 @@ def test_composition_discovery_and_capacity_limits_are_not_blanket_successes(sel dict(report, unmet_capacity_targets=[]), dict(report, adoption_status='all_imports_qualified')]: self.assertFalse(check_result(command, 0, json.dumps(broken))[0]) + # Combined-source checks must retain both independently owned optional products. + for module, count, command in [ + ('JSONNavigationTests', 47, ['swift', 'test', '--filter', 'navigation']), + ('JSONInspectionTests', 44, ['swift', 'test', '--filter', 'inspection']), + ('JSONSchemaExamplesTests', 98, ['swift', 'test', '--test-product', 'JSONSchemaExamplesTests']), + ]: + events = dict(bundle_counts=[count], module_function_counts={module: count}, + parameter_case_invocations=0) + self.assertTrue(check_result(command, 0, '', events)[0]) + for broken in [dict(events, bundle_counts=[count-1]), + dict(events, module_function_counts={'WrongModule': count}), + dict(events, parameter_case_invocations=1)]: + self.assertFalse(check_result(command, 0, '', broken)[0]) + policy = DISCOVERY + for key in ('root_functions', 'portable_root_functions', 'wasi_root_functions'): + self.assertEqual(policy[key]['JSONNavigationTests'], 47) + self.assertEqual(policy[key]['JSONSchemaExamplesTests'], 98) + self.assertEqual(policy[key]['JSONInspectionTests'], 44) + self.assertEqual(policy[key]['JSONIntegrationTests'], 32) + for key in ('shared_state_functions', 'portable_shared_state_functions'): + self.assertEqual(policy[key]['JSONNavigationTests'], 1) + self.assertEqual(policy[key]['JSONSchemaExamplesTests'], 4) + self.assertEqual(policy[key]['JSONInspectionTests'], 3) + self.assertEqual(policy[key]['JSONIntegrationTests'], 11) + def setUp(self): self.temporary = tempfile.TemporaryDirectory() self.addCleanup(self.temporary.cleanup) @@ -126,6 +151,12 @@ def test_exact_discovery_rejects_missing_duplicate_and_changed_parameters(self): self.assertTrue(check_result(harness, 0, 'HARNESS_TESTS passed cases=15')[0]) for stale in ['HARNESS_TESTS passed cases=10', 'HARNESS_TESTS passed cases=11', 'HARNESS_TESTS passed cases=12', 'HARNESS_TESTS passed cases=13', 'HARNESS_TESTS passed cases=14', '']: self.assertFalse(check_result(harness, 0, stale)[0]) + lifetime = ['python3', '-B', 'Scripts/qualify_schema_example_lifetime.py'] + marker = 'SCHEMA_EXAMPLES_CORPUS passed mode=owned scopes=128 values=6 exact=1' + self.assertTrue(check_result(lifetime, 0, marker)[0]) + self.assertFalse(check_result(lifetime, 1, marker)[0]) + for stale in ['', marker.replace('128', '1'), marker.replace('exact=1', 'exact=0')]: + self.assertFalse(check_result(lifetime, 0, stale)[0]) # Scoped portability lanes retain exact discovery and safety rather # than describing an exit-zero subset as full qualification. output = pathlib.Path(__file__).resolve().parents[1] / 'Artifacts/HarnessProbe' diff --git a/Scripts/verify_schema_example_documentation.py b/Scripts/verify_schema_example_documentation.py new file mode 100644 index 0000000..4f1867e --- /dev/null +++ b/Scripts/verify_schema_example_documentation.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +"""Reject documentation drift/count/import/receipt mutations; verify all 49 scope rows.""" +import json +import pathlib +import shutil +import tempfile +from unittest.mock import patch + +import ci +import schema_example_documentation as docs + +ROOT = pathlib.Path(__file__).resolve().parents[1] + + +def main(): + rows = [] + assert docs.check()['snippets'] == 10 + rows.append({'case': 'actual-ten-snippets', 'accepted': True}) + source = (docs.CATALOG / 'VerifiedCandidates.md').read_text() + for name, change in [ + ('missing-block', lambda s: s.replace('```swift', '```text', 1)), + ('extra-block', lambda s: s + '\n```swift\nprecondition(true)\n```\n'), + ('empty-block', lambda s: s + '\n```swift\n\n```\n'), + ('foreign-import', lambda s: s.replace('import JSON\n', 'import Foundation\n', 1)), + ('changed-authored-code', lambda s: s.replace('result.attempts == 1', 'result.attempts == 2', 1)), + ('stale-consumer', lambda s: s), + ]: + with tempfile.TemporaryDirectory(prefix='swift-json-docs-') as temporary: + root = pathlib.Path(temporary) + catalog = root / 'Sources/JSONSchemaExamples/Documentation.docc' + shutil.copytree(docs.CATALOG, catalog) + (catalog / 'VerifiedCandidates.md').write_text(change(source)) + consumer = root / docs.CONSUMER.relative_to(ROOT) + consumer.parent.mkdir(parents=True) + consumer.write_text(docs.CONSUMER.read_text() + ('\n// stale\n' if name == 'stale-consumer' else '')) + with patch.object(docs, 'ROOT', root), patch.object(docs, 'CATALOG', catalog), patch.object(docs, 'CONSUMER', consumer): + try: + docs.check() + except ValueError: + rows.append({'case': name, 'accepted': False}) + else: + raise ValueError('Accepted documentation mutation: ' + name) + command = ['python3', '-B', 'Scripts/qualify_schema_examples.py'] + actual = 'SCHEMA_EXAMPLES_COMPILER passed public_cases=15 negatives=2\nSCHEMA_EXAMPLES_DOCUMENTATION_COMPILER passed snippets=10 processes=2' + for name, code, log, expected in [ + ('actual-receipt', 0, actual, True), + ('missing-documentation-outcome', 0, actual.splitlines()[0], False), + ('wrong-snippet-count', 0, actual.replace('snippets=10', 'snippets=11'), False), + ('nonzero-command', 1, actual, False), + ]: + accepted = ci.check_result(command, code, log)[0] + assert accepted == expected, name + rows.append({'case': name, 'accepted': accepted}) + policy = json.loads((ROOT / 'Documentation/Architecture/ResourceProfiles.json').read_text()) + metrics = [m.get('metric', m.get('id')) for k in ['core','schema','streaming','patch','enhancement_limits'] for m in policy[k]] + classified = [] + for line in (ROOT / 'Documentation/Architecture/Release1.6/SchemaExamplesLimits.md').read_text().splitlines(): + if not line.startswith('| ') or line.startswith(('| Metrics ', '| ---')): + continue + names = line.split('|')[1].strip().split(', ') + classified += names + assert len(classified) == len(set(classified)) == 49 and set(classified) == set(metrics), classified + assert len(rows) == 11 + print(json.dumps({'status': 'passed', 'checks': rows, 'scope_metrics': 49}, indent=2)) + print('SCHEMA_EXAMPLES_DOC_HARNESS passed cases=11 metrics=49') + + +if __name__ == '__main__': + main() diff --git a/Scripts/wasm_support.py b/Scripts/wasm_support.py index 3f2becc..d5bd26a 100644 --- a/Scripts/wasm_support.py +++ b/Scripts/wasm_support.py @@ -68,7 +68,7 @@ def binary(scratch, config, name): if preamble and not re.fullmatch(r"Another instance of SwiftPM .*waiting until that process has finished execution\.\.\.", preamble): raise ValueError('Unexpected manifest preamble: ' + preamble) manifest = json.loads(manifest_text[start:]) - if manifest['dependencies'] or sorted(p['name'] for p in manifest['products']) != ['JSON', 'JSONBridges', 'JSONHTML', 'JSONInspection', 'JSONNavigation', 'JSONPatch', 'JSONSchema', 'JSONStreaming', 'swift-json']: + if manifest['dependencies'] or sorted(p['name'] for p in manifest['products']) != ['JSON', 'JSONBridges', 'JSONHTML', 'JSONInspection', 'JSONNavigation', 'JSONPatch', 'JSONSchema', 'JSONSchemaExamples', 'JSONStreaming', 'swift-json']: raise ValueError('Changed public products/dependencies') def invoke(engine, path, mode='command', arguments=(), repo=True, resources=True): if engine == 'node': diff --git a/Sources/JSONCLIEngine/CLIBuildIdentity.swift b/Sources/JSONCLIEngine/CLIBuildIdentity.swift index 14f5939..a4d5048 100644 --- a/Sources/JSONCLIEngine/CLIBuildIdentity.swift +++ b/Sources/JSONCLIEngine/CLIBuildIdentity.swift @@ -2,6 +2,6 @@ // Generated by Scripts/generate_cli_identity.py; excludes this file. // SHA-256 of the ordered Package.swift + Sources content manifest, not a Git commit. package enum CLIBuildIdentity { - package static let revision = "sha256:cbd05c9a19bfe75ffc2a3e2d6b84b91172cbf5aa03845440ecbdc5af424b1be7" + package static let revision = "sha256:81f4a851840b1c2e51064548b3e28f2682f2c7b50867b08f251422c4601aeb30" package static let kind = "source-sha256" } diff --git a/Sources/JSONInspection/Documentation.docc/JSONInspection.md b/Sources/JSONInspection/Documentation.docc/JSONInspection.md index 41ab5b6..5b58e79 100644 --- a/Sources/JSONInspection/Documentation.docc/JSONInspection.md +++ b/Sources/JSONInspection/Documentation.docc/JSONInspection.md @@ -24,3 +24,11 @@ or numeric matrix. Sampling does not exempt any source from admission. - ``JSONShapeKindCounts`` - ``JSONColumnObservation`` - ``JSONShapeInspectionFailure`` + +### Capture complete source structure + +- +- ``JSONStructuralSnapshotBuilder`` +- ``JSONStructuralSnapshot`` +- ``JSONStructuralRecord`` +- ``JSONStructuralSnapshotFailure`` diff --git a/Sources/JSONInspection/Documentation.docc/StructuralSnapshots.md b/Sources/JSONInspection/Documentation.docc/StructuralSnapshots.md new file mode 100644 index 0000000..26e8359 --- /dev/null +++ b/Sources/JSONInspection/Documentation.docc/StructuralSnapshots.md @@ -0,0 +1,85 @@ +# Index complete physical structure + +Build source-attached metadata without replacing the canonical JSON document. + +## Capture even a scalar root + +```swift +let source = JSONDocument(root: .null) +let report = try JSONStructuralSnapshotBuilder(document: source).build() +let snapshot = report.result +let root = try snapshot.record(at: 0) +precondition(snapshot.recordCount == 1) +precondition(root.parentOrdinal == nil && root.componentFromParent == nil) +precondition(root.depth == 0 && root.childCount == 0 && root.subtreeEndExclusive == 1) +let value = try snapshot.value(for: 0) +precondition(value.result == .null) +``` + +The builder always covers one entire physical root, including null and empty +containers. No filter, prefix, prune, depth selection or caller callback can return +an incomplete snapshot. A negative maximumRecords fails configuration before +admission. Zero refuses the real root with a typed capacity error; there is no +successful empty or partial artifact. Quota exhaustion and cancellation throw. + +## Keep paths and numbers exact + +```swift +let source = try JSONParser().parseDocument(Data(#"{"":null,"0":[9007199254740993,0.0100]}"#.utf8)) +let snapshot = try JSONStructuralSnapshotBuilder(document: source).build().result +let rootPath = try snapshot.location(for: 0).result +let emptyKeyPath = try snapshot.location(for: 1).result +let arrayElementPath = try snapshot.location(for: 3).result +precondition(rootPath.isEmpty && emptyKeyPath == [.key("")]) +precondition(arrayElementPath == [.key("0"), .index(0)]) +let integer = try snapshot.value(for: 3).result.numberValue!.integer(as: UInt64.self) +let lexeme = try snapshot.value(for: 4).result.numberValue!.originalLexeme +precondition(integer == 9_007_199_254_740_993 && lexeme == "0.0100") +let root = try snapshot.record(at: 0) +precondition(root.subtreeEndExclusive == snapshot.recordCount) +``` + +Object members retain insertion order and decoded scalar key identity. Array +indices increase from zero. Root [] differs from an empty key; key "0" differs +from index 0. Parent ordinals are lower than children; finalized half-open subtree +ranges include the node and all its descendants, not merely direct children. +Numbers remain in the exact canonical source, with no binary64 projection. + +## Preserve source binding and explicit lookup budgets + +```swift +let document = try JSONParser().parseDocument(Data(#"{"old":null}"#.utf8)) +let snapshot = try JSONStructuralSnapshotBuilder(document: document).build().result +let replacement = document.replacingRoot(.array([42])) +let newer = try JSONStructuralSnapshotBuilder(document: replacement).build().result +let oldPath = try snapshot.location(for: 1) +let newPath = try newer.location(for: 1) +precondition(oldPath.result == [.key("old")] && newPath.result == [.index(0)]) +let oldSpan = try snapshot.sourceSpan(for: 1) +let newSpan = try newer.sourceSpan(for: 1) +precondition(oldSpan.result != nil && newSpan.result == nil) +precondition(oldPath.statistics[.nodes] == 2 && newPath.statistics[.nodes] == 2) +``` + +Ordinals are positions local to the particular snapshot method being called. +A record carries metadata, not a portable node identity. No method accepts a +replacement source or detached handle. An older snapshot keeps its original +immutable source and coherent map; it never follows another document’s revision. +Only original mapped spans are available. Invalid ordinals throw typed bounds +errors before admission rather than returning null, missing or a default record. + +Construction uses Navigation’s original single cumulative read scope and meter. +It transfers the already-admitted document ownership once after complete range +finalization, retaining one source and parent/edge records, without per-node JSON +values, full paths or source maps. Logical overlay v1 reserves 128 fixed bytes, +96 per record, exact edge-key UTF-8 bytes and 16 per open frame, plus original +source/path costs. These policy weights are not Swift heap layout or process RSS. + +record(at:) is O(1) metadata access without opening a new ledger. Each location, +value or sourceSpan call deliberately opens a separate finite operation and +re-admits the whole captured document, even for root or unmapped-span lookup. +Repeated K lookups pay K admissions; they do not share the builder’s remainder. +Path reconstruction also uses depth-sensitive core prefix copies and selection. +The existing traversal builds full hit paths, so total snapshot build work is +not guaranteed linear in node count. No batch session, persistence, stable source +fingerprint, table certification or new JSON representation is provided. diff --git a/Sources/JSONInspection/JSONStructuralSnapshot.swift b/Sources/JSONInspection/JSONStructuralSnapshot.swift index 5ad64c3..52ba154 100644 --- a/Sources/JSONInspection/JSONStructuralSnapshot.swift +++ b/Sources/JSONInspection/JSONStructuralSnapshot.swift @@ -2,42 +2,69 @@ import JSON import JSONNavigation -/// Experimental metadata only; ordinal identity is local to one snapshot. -package struct JSONStructuralRecord: Sendable { - package let ordinal: Int - package let parentOrdinal: Int? - package let componentFromParent: JSONLocationComponent? - package let kind: JSONTraversalKind - package let depth: Int - package let childCount: Int - package internal(set) var subtreeEndExclusive: Int +/// Read-only DFS preorder metadata for one physical node in a complete snapshot. +/// Ordinals are local positions in the containing snapshot, not transferable node +/// identities. A record cannot resolve a value or attach to a replacement source. +public struct JSONStructuralRecord: Sendable { + /// Zero-based preorder position within the containing snapshot. + public let ordinal: Int + /// Parent position, strictly lower than this ordinal; nil only for the root. + public let parentOrdinal: Int? + /// Exact key or array index from the parent; nil only for the physical root. + public let componentFromParent: JSONLocationComponent? + /// Canonical JSON kind; no inferred schema, missing or table kind. + public let kind: JSONTraversalKind + /// Number of physical edges from the root, whose depth is zero. + public let depth: Int + /// Number of immediate children; a scalar or empty container has zero. + public let childCount: Int + /// Finalized exclusive preorder end; this node’s subtree is [ordinal, end). + public internal(set) var subtreeEndExclusive: Int } -package enum JSONStructuralSnapshotFailure: Error, Sendable, Equatable { +/// A capacity or local-position refusal; no partial snapshot or null substitute. +public enum JSONStructuralSnapshotFailure: Error, Sendable, Equatable { + /// The next physical node exceeds the inclusive record bound. + /// - Parameter limit: Nonnegative maximum configured for this build. case recordLimitExceeded(limit: Int) + /// The supplied local position is outside 0.. JSONStructuralRecord { + /// Reads one metadata record in O(1), without opening a new resource ledger. + /// This trusted-memory access performs no path/value scan or owned growth. + /// - Parameter ordinal: Position local to this snapshot, not a detached node ID. + /// - Returns: Read-only metadata with a finalized half-open subtree range. + /// - Throws: `JSONStructuralSnapshotFailure.ordinalOutOfBounds` for an invalid position. + public func record(at ordinal: Int) throws -> JSONStructuralRecord { guard ordinal >= 0, ordinal < records.count else { throw JSONStructuralSnapshotFailure.ordinalOutOfBounds(ordinal) } return records[ordinal] } - /// Separate owned lookup; all parent-chain and path work uses its own scope. - package func location(for ordinal: Int, limits: JSONLimits = .balanced) + /// Reconstructs an exact typed location in a separate bounded operation. + /// Re-admits the entire captured root/source/map before parent-chain/path work. + /// Core prefix copying can be quadratic in depth; this is not O(1) lookup. + /// - Parameters: + /// - ordinal: Position local to this snapshot; bounds checked before admission. + /// - limits: Finite quotas for this new lookup, independent of construction. + /// - Returns: Exact key/index path and cumulative lookup statistics; [] is root. + /// - Throws: Typed bounds, core resource/configuration failures or cancellation. + public func location(for ordinal: Int, limits: JSONLimits = .balanced) throws -> JSONOperationReport<[JSONLocationComponent]> { try lookup(ordinal, limits: limits) { path, state, _ in // Output path ownership survives scope completion's temporary refund. @@ -51,9 +78,16 @@ package struct JSONStructuralSnapshot: Sendable { } } - /// Resolve only against this captured source, using the core strict selector. - /// Returned JSON follows existing read-value alias semantics, not a copied DOM. - package func value(for ordinal: Int, limits: JSONLimits = .balanced) + /// Resolves a value only against the captured source in a new bounded operation. + /// Each call re-admits the full source and reconstructs its typed path before + /// core selection. The result uses existing immutable read-value alias semantics; + /// no JSON subtree is cached in a record or converted through binary floating point. + /// - Parameters: + /// - ordinal: Position local to this snapshot; invalid positions never become null. + /// - limits: Finite quotas for this separate lookup, not the builder’s remainder. + /// - Returns: The canonical value, including explicit null, and lookup statistics. + /// - Throws: Typed bounds, core access/resource/configuration errors or cancellation. + public func value(for ordinal: Int, limits: JSONLimits = .balanced) throws -> JSONOperationReport { try lookup(ordinal, limits: limits) { path, state, scope in try state.meter.work() @@ -61,7 +95,15 @@ package struct JSONStructuralSnapshot: Sendable { } } - package func sourceSpan(for ordinal: Int, limits: JSONLimits = .balanced) + /// Looks up a coherent original value span in a new bounded operation. + /// Full captured-source admission and path reconstruction occur even without a + /// source map. A replacement/unmapped document has no invented historical span. + /// - Parameters: + /// - ordinal: Position local to this snapshot; bounds checked before admission. + /// - limits: Finite quotas for this separate source-map lookup. + /// - Returns: Original mapped span or nil when unavailable, plus lookup statistics. + /// - Throws: Typed bounds, core resource/configuration failures or cancellation. + public func sourceSpan(for ordinal: Int, limits: JSONLimits = .balanced) throws -> JSONOperationReport { try lookup(ordinal, limits: limits) { path, state, _ in try document.sourceMap?.span(at: path, kind: .value, meter: &state.meter) @@ -104,16 +146,35 @@ package struct JSONStructuralSnapshot: Sendable { } } -/// Complete physical-root only; no filter, prune, prefix or custom callback. -package struct JSONStructuralSnapshotBuilder: Sendable { - package let document: JSONDocument - package let limits: JSONLimits +/// Builds complete source-attached metadata with one original cumulative ledger. +/// No filtering, pruning, prefix, callback, per-node JSON copy or second admission. +/// Records retain parent/edge metadata and one immutable document. Existing walker +/// hit paths mean total construction is depth-sensitive, not guaranteed O(nodes). +/// Logical overlay v1 is 128 fixed bytes, 96/record, exact UTF-8 edge-key bytes and +/// 16/live open frame, plus source/path admission. These weights are not heap/RSS. +public struct JSONStructuralSnapshotBuilder: Sendable { + /// Canonical immutable source; admitted once when build begins. + public let document: JSONDocument + /// Finite construction quotas shared by admission, traversal and finalization. + public let limits: JSONLimits package var checkpointObserver: (@Sendable (UInt64) -> Void)? package var lifetimeObserver: (@Sendable (Bool, Bool) -> Void)? - package init(document: JSONDocument, limits: JSONLimits = .balanced) { + /// Captures trusted source and policy; this initializer performs no admission. + /// - Parameters: + /// - document: Physical canonical root, optionally with coherent source mapping. + /// - limits: One finite profile for the complete construction invocation. + public init(document: JSONDocument, limits: JSONLimits = .balanced) { self.document = document; self.limits = limits } - package func build(maximumRecords: Int = 100_000) + /// Returns every physical node with finalized ranges, or throws without an artifact. + /// Negative capacity fails before admission; zero refuses the real root. Even + /// null and empty containers have one record. Source ownership transfers once + /// only after complete traversal; temporary scope references are invalidated. + /// - Parameter maximumRecords: Inclusive nonnegative bound, default 100,000. + /// - Returns: Immutable complete snapshot and cumulative construction statistics. + /// - Throws: Typed capacity refusal, invalid configuration, resource exhaustion + /// or cancellation. No successful partial snapshot or terminal-error override. + public func build(maximumRecords: Int = 100_000) throws -> JSONOperationReport { guard maximumRecords >= 0 else { throw JSONFailure(.configuration, code: .invalidLimits) } var reducer = SnapshotReducer(maximumRecords: maximumRecords) diff --git a/Sources/JSONSchema/Compilation/JSONSchemaCompiler.swift b/Sources/JSONSchema/Compilation/JSONSchemaCompiler.swift index f1afd60..b9487d8 100644 --- a/Sources/JSONSchema/Compilation/JSONSchemaCompiler.swift +++ b/Sources/JSONSchema/Compilation/JSONSchemaCompiler.swift @@ -66,6 +66,12 @@ public struct JSONSchemaCompiler: Sendable { var state = JSONSchemaCompilation(options: options) return try state.compile(document, registry: registry, meter: &meter) } + /// Package composition preserves the admitted document and its source map. + package func compile(_ document: JSONSchemaDocument, registry: JSONSchemaRegistry, + meter: inout JSONWorkMeter) throws -> CompiledJSONSchema { + var state = JSONSchemaCompilation(options: options) + return try state.compile(document, registry: registry, meter: &meter) + } } /// Invocation-owned mutable graph builder. Every lookup remains exact and charged. diff --git a/Sources/JSONSchema/Compilation/JSONSchemaQualifiedNode.swift b/Sources/JSONSchema/Compilation/JSONSchemaQualifiedNode.swift new file mode 100644 index 0000000..3b84126 --- /dev/null +++ b/Sources/JSONSchema/Compilation/JSONSchemaQualifiedNode.swift @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSON + +/// Read-only construction access to compiler-qualified declarations. Handles and +/// targets can only originate in the same immutable plan; no URI is reparsed here. +package struct JSONSchemaQualifiedNode: Sendable { + private let plan: JSONSchemaPlan + private let index: Int + fileprivate init(plan: JSONSchemaPlan, index: Int) { self.plan = plan; self.index = index } + + /// Plan-local identity, not a stable/public schema identifier. + package var identity: Int { index } + package var childCount: Int { plan.nodes[index].children.count } + package var referenceCount: Int { plan.nodes[index].references.count } + + package struct Declaration: Sendable { + package let raw: JSON + package let documentPointer: JSONPointer + package let resourceURI: JSONSchemaURI + package let vocabulary: JSONSchemaVocabulary + package let formatAssertion: Bool + } + package struct Child: Sendable { + package let keyword: JSONSchemaPlan.Keyword + package let token: JSONKey? + package let target: JSONSchemaQualifiedNode + } + package struct Reference: Sendable { + package let keyword: JSONSchemaPlan.Keyword + package let requiresDynamicScope: Bool + package let target: JSONSchemaQualifiedNode + } + package func declaration(meter: inout JSONWorkMeter) throws -> Declaration { + try meter.work() + let node = plan.nodes[index] + return .init(raw: node.raw, documentPointer: node.pointer, + resourceURI: plan.resources[node.resource].uri, + vocabulary: node.vocabulary, formatAssertion: node.formatAssertion) + } + package func child(at offset: Int, meter: inout JSONWorkMeter) throws -> Child { + try meter.work() + guard plan.nodes[index].children.indices.contains(offset) else { + throw JSONFailure(.configuration, code: .invalidOperation) + } + let edge = plan.nodes[index].children[offset] + return .init(keyword: edge.keyword, token: edge.token, + target: .init(plan: plan, index: edge.target)) + } + package func reference(at offset: Int, meter: inout JSONWorkMeter) throws -> Reference { + try meter.work() + guard plan.nodes[index].references.indices.contains(offset) else { + throw JSONFailure(.configuration, code: .invalidOperation) + } + let edge = plan.nodes[index].references[offset] + return .init(keyword: edge.keyword, requiresDynamicScope: edge.dynamicAnchor != nil, + target: .init(plan: plan, index: edge.target)) + } +} + +extension CompiledJSONSchema { + package var exampleRoot: JSONSchemaQualifiedNode { .init(plan: plan, index: plan.root) } +} diff --git a/Sources/JSONSchemaExamples/Documentation.docc/AdvancedConstruction.md b/Sources/JSONSchemaExamples/Documentation.docc/AdvancedConstruction.md new file mode 100644 index 0000000..7a594d9 --- /dev/null +++ b/Sources/JSONSchemaExamples/Documentation.docc/AdvancedConstruction.md @@ -0,0 +1,54 @@ +# Conservative advanced proposals + +Use bounded hints while keeping the complete native schema as the authority. + +```swift +import JSON +import JSONSchema +import JSONSchemaExamples + +let document = try JSONSchemaDocument(JSONParser().parse( + #"{"type":"array","minItems":2,"uniqueItems":true,"items":{"type":"string"},"contains":{"pattern":"^SKU-[0-9]{4}$"},"minContains":2,"maxContains":2}"# +)) +let result = try JSONSchemaExampleGenerator(document: document).generate(seed: 42) +precondition(result.validation.isValid) +``` + +The synchronized public guide consumer and native tests execute this composed shape. Contains hints request enough items +and add the qualified contains child beside each applicable prefix/items child. +Several conjunctions remain separate. Full native validation counts actual matches, +checks maxima and propagates evaluated-item annotations. The constructor does not +solve arbitrary matching partitions or prevent every possible surplus match. + +The pattern helper only proposes fully anchored ASCII sequences of printable +literals, escaped regex punctuation, single ASCII ranges and exact `{n}` repeats. +It neither matches strings nor replaces the native regex engine. Unsupported +patterns can use finite admitted corpus/filler candidates. No Foundation regex, +external parser, unbounded fallback or new runtime dependency is introduced. + +## Honest incomplete search + +AllOf, oneOf, conditionals, dependencies, property-name/pattern rules and +unevaluated-location behavior are checked against the original root. Empty optional +objects or a later finite variation can satisfy some schemas; that does not +establish general constructive support for those keywords. A oneOf branch candidate +matching two branches is rejected and its evaluation work remains charged. + +Static external references require explicit immutable registry resources. Compiler +identities preserve base URI and anchor transitions. Reaching a scope-dependent +dynamic reference fails explicitly; omitting an optional dynamic child may yield a +valid finite witness without implementing constructive dynamic navigation. + +The G4 public consumer executes ten additional must-generate cases and eight +may-fail cases across two profiles/four seeds, recording every outcome. A may-fail +case may return a fully verified witness or explicit unsupported/exhausted result. +It cannot turn search failure into an unsatisfiability proof. Native resource +errors, compilation errors and cancellation remain operational failures. + +All scans, repeat digits, staging and completed false verdicts use the same finite +generation ledger. Returned statistics describe successful calls only; no value +or successful report is invented after a failure. Logical retained bytes are not +RSS and caller qualification work is separate. Algorithm is s3-candidates-v4; +the trace v2 grammar and source/corpus/policy v1 transports remain unchanged. +See for the qualified native lifecycle. G7/G8 final +platform/integration/release qualification remains separate. diff --git a/Sources/JSONSchemaExamples/Documentation.docc/DeterministicConstruction.md b/Sources/JSONSchemaExamples/Documentation.docc/DeterministicConstruction.md new file mode 100644 index 0000000..d916164 --- /dev/null +++ b/Sources/JSONSchemaExamples/Documentation.docc/DeterministicConstruction.md @@ -0,0 +1,87 @@ +# Deterministic construction + +Retain exact values, seed choices explicitly and identify every input to a decision. + +## Choose a profile and corpus + +```swift +import JSON +import JSONSchema +import JSONSchemaExamples + +let corpus = JSONSchemaExampleCorpus(values: [.string("example"), .null]) +let prepared = try JSONSchemaExampleGenerator( + document: JSONSchemaDocument(JSONParser().parse("true")), + options: .init(corpus: corpus) +) +let result = try prepared.generate(seed: 42, profile: .demonstration) +precondition(result.validation.isValid) +precondition(result.corpusDigest == prepared.corpusDigest) +precondition(result.randomDraws > 0) +``` + +The runnable public consumer exercises both profiles and a corpus with exact +large decimals and scalar-distinct keys. Minimal scans declared candidates in +stable order. Demonstration chooses an unbiased starting offset and scans +circularly, without replacement. It uses corpus tokens for true/empty schemas. +Neither profile claims to synthesize arbitrary constrained values or vary output +for every seed. Custom tokens never bypass native validation. + +## Exact construction + +The generator uses the existing iterative strict writer and parser with its one +invocation meter. Returned objects have scalar UTF-8 key order, numbers have +normalized exact spelling and arrays retain their nesting/order. Equivalent source +member orders and numeric spellings yield the same emitted bytes. NFC/NFD scalar +spellings remain distinct. No binary floating point or String dictionary intervenes. +Depth, value admission, construction, rejected choices and all native validations +consume cumulative work. Prepared plans can be shared; mutable PRNG and trace +state belong to each call. + +## Versioned identities + +`s3-candidates-v4` records `splitmix64-v1`, seed and profile. Snapshot, ordered +corpus, all policy caps/flags and complete abstract trace have independent versioned +SHA-256 identities. See the Release1.6/G2Determinism and G3Construction contracts for the byte formats. +These transports use compact scalar-key normalized-number strict JSON, not JCS +or unavailable original source bytes. Caller registry order is part of snapshot +identity. Policy identity includes all 49 metrics for each of generation, +compilation and evaluation; compilation assertion policy remains distinct. + +Trace records contain fixed codes and unsigned integers, including every raw +random draw and completed verdict. No arbitrary schema/instance text or diagnostic +message is logged. Every byte shares the cumulative diagnostic byte quota with +native diagnostics. A successful trace is complete; exhaustion/cancellation throws +without a certificate. Hash work and result metadata also fit before return. +Logical retained bytes do not describe process RSS, and owned work quotas do not +limit caller code. Native golden vectors and separate-process checks establish +the native G5 increment; full Linux/WASI/mobile qualification remains G7. + + +G3 adds conservative typed value requests to the trace and uses +`swift-json-example-trace-v2`. The original snapshot/corpus/policy transports stay +at v1. Historical G2 vectors retain their earlier IDs in source history and the +immutable G2 review archive. A new algorithm version can change witnesses/traces; +reproducibility requires the same implementation and versioned inputs. + +G4 extends proposals while retaining the trace v2 grammar; its envelope binds +s3-candidates-v4. Historical G3 witnesses/traces remain in the G3 source/archive. + +## Replay the same prepared inputs + +```swift +let prepared = try JSONSchemaExampleGenerator( + document: JSONSchemaDocument(JSONParser().parse(#"{"type":"string","minLength":3}"#)) +) +let first = try prepared.generate(seed: 42, profile: .demonstration) +let second = try prepared.generate(seed: 42, profile: .demonstration) +precondition(first.value == second.value && first.traceDigest == second.traceDigest) +precondition(first.statistics == second.statistics) +precondition(first.sourceSnapshotDigest == prepared.sourceSnapshotDigest) +precondition(first.policyDigest == prepared.policyDigest && first.corpusDigest == prepared.corpusDigest) +``` + +Retain the implementation commit as well as algorithm ID, seed/profile, source, +ordered registry, corpus and policies. Identical source digests alone do not bind +evaluator policies; equal seeds need not produce different values. There is no +locale, clock, system randomness, online lookup or process-randomized Hasher input. diff --git a/Sources/JSONSchemaExamples/Documentation.docc/JSONSchemaExamples.md b/Sources/JSONSchemaExamples/Documentation.docc/JSONSchemaExamples.md new file mode 100644 index 0000000..8742af2 --- /dev/null +++ b/Sources/JSONSchemaExamples/Documentation.docc/JSONSchemaExamples.md @@ -0,0 +1,44 @@ +# ``JSONSchemaExamples`` + +Deterministic exact JSON candidates certified by the existing offline schema validator. + +Use this optional 1.6 development library for schema-driven fixtures, examples and +tooling where every returned value must satisfy the original compiled root. Prepare +an admitted JSONSchemaDocument with explicit offline resources, then generate with +finite policies and a reproducible seed. JSON and JSONSchema keep their existing +semantics and acquire no dependency on this module. + +Construction is conservative: exact declared hints, bounded typed values, static +references and qualified composition, contains and anchored ASCII-pattern proposals. +Every candidate requires completed native root validation. Unsupported or exhausted +search is explicit; this is not a complete satisfiability solver. Formats, defaults, +requiredness, dynamic scope and annotations retain the native dialect's meanings. + +All Swift guide blocks execute through a separate public consumer. Native G1-G6 +incremental evidence is separate from remaining G7/G8 release/platform qualification. +The standalone source checkout is the API authority until this product is released. + +## Topics + +### Start with an offline schema + +- +- ``JSONSchemaExampleGenerator`` +- ``JSONSchemaExampleOptions`` +- ``JSONSchemaExampleResult`` + +### Construct exact, reproducible candidates + +- +- +- +- ``JSONSchemaExampleProfile`` +- ``JSONSchemaExampleCorpus`` + +### Bound work and handle failures + +- +- ``JSONSchemaExampleFailure`` +- ``JSONSchemaExampleUnsupportedFeature`` +- ``JSONSchemaUnsatisfiableProof`` +- ``JSONSchemaExampleConfigurationIssue`` diff --git a/Sources/JSONSchemaExamples/Documentation.docc/PoliciesAndLimits.md b/Sources/JSONSchemaExamples/Documentation.docc/PoliciesAndLimits.md new file mode 100644 index 0000000..5456120 --- /dev/null +++ b/Sources/JSONSchemaExamples/Documentation.docc/PoliciesAndLimits.md @@ -0,0 +1,107 @@ +# Policies, limits and lifecycle + +Keep preparation separate, bound every generation attempt cumulatively and treat errors as terminal. + +## Configure finite budgets and read remaining work + +``JSONSchemaExampleOptions/compilationOptions`` governs the whole preparation: +compilation plus source, corpus and policy serialization/hashing. Generation uses +the per-metric intersection of ``JSONSchemaExampleOptions/limits`` and +``JSONSchemaExampleOptions/validationOptions`` limits. Both default to balanced; +maximumAttempts defaults to 16 and counts completed invalid validations too. + +```swift +import JSON +import JSONSchema +import JSONSchemaExamples + +var limits = JSONLimits.conservative +limits[.workUnits] = 2_000_000 +var evaluation = JSONSchemaOptions(limits: limits) +evaluation.collectAnnotations = true +let options = JSONSchemaExampleOptions( + limits: limits, maximumAttempts: 4, validationOptions: evaluation +) +let prepared = try JSONSchemaExampleGenerator( + document: JSONSchemaDocument(JSONParser().parse(#"{"enum":[0,"ok"],"type":"string"}"#)), + options: options +) +let result = try prepared.generate() +let ceiling = min(prepared.options.limits[.workUnits], + prepared.options.validationOptions.limits[.workUnits]) +let used = result.statistics[.workUnits] +precondition(used <= ceiling) +let remainingWork = ceiling - used +precondition(remainingWork < ceiling && result.attempts == 2) +precondition(result.statistics[.evaluationWorkUnits] > 0) +``` + +Discovery, construction, normalization, every native validation and final trace +hashing share one invocation meter. False verdicts keep their evaluation/numeric/ +regex work and issue/diagnostic charges. Subsystem work is already included in +workUnits: do not subtract it again. Work, numeric work, evaluation work and +aggregateRegexWorkUnits never reset between candidates or branches. A separate +public generate call starts an independent invocation, not a continuation of a +previous report. To bound a larger caller composition, carry checked remaining +work and subsystem ceilings across its successful calls. PreparationStatistics is +separate and must fit its preparation policy; do not add memory peaks together. + +## Preserve evaluator policy and raw metadata + +Format is annotation by default. The generator forwards explicit validation options +unchanged; a required format-assertion vocabulary remains authoritative. Compilation +assertion policy is separately retained. Defaults and examples only offer candidates. +The generator neither fills defaults nor upgrades raw metadata into constraints. + +```swift +let document = try JSONSchemaDocument(JSONParser().parse( + #"{"const":"not-an-email","format":"email","default":"example@example.com"}"# +)) +let annotated = try JSONSchemaExampleGenerator(document: document).generate() +precondition(annotated.value == .string("not-an-email")) +precondition(!annotated.validationOptions.formatAssertion) +let plan = try JSONSchemaCompiler().compile(document) +let asserted = try plan.validate(annotated.value, options: .init(formatAssertion: true)) +precondition(!asserted.isValid) +precondition(document.raw.objectValue?["default"] == .string("example@example.com")) +precondition(!annotated.compilationOptions.formatAssertion) +``` + +Do not combine a result with another plan, registry or policy and imply the original +certificate applies. Independent validation above is application verification work, +separate from the completed generation report. + +## Capacity, retention and terminal state + +All 49 existing profile entries stay finite. Individual strings, numeric tokens, +containers, depth and active frames have capacity ceilings. Per-materialization +input/output/node/text capacities retire between candidates while report totals and +charged work are preserved. Per-search regex work has its existing search ceiling; +aggregate regex work remains cumulative. Compilation-only, streaming and Patch +metrics do not imply that generation performs those operations. No new unlimited +setting or generated-node metric is introduced. + +``JSONSchemaExampleResult/statistics`` snapshots certification, including the returned +value/verdict, metadata and conservative staging reservations. Some scratch reservations +live until the proposal or invocation ends even when Swift can release a temporary +earlier. Rejected ownership is released without refunding work. Remaining invocation +reservations end on result transfer or failure. Prepared source/registry/corpus/plan +ownership lasts as long as the caller retains the immutable generator. A returned +value remains usable after preparation leaves scope; it holds no generator or search stack. + +Logical retained bytes and peakLogicalRetainedBytes are accounting weights, not heap +size, allocator reclamation timing or RSS. Owned work quotas do not cap arbitrary +caller callbacks or instructions and are not wall-clock deadlines. + +Native operational errors and cancellation immediately end the invocation. Cleanup +preserves the first terminal failure and consumed counters. No partial candidate or +successful report escapes; retrying with a fresh full quota would lose a caller's +cumulative bound. Cancellation is checked at entry, before owned growth and inside +numeric, regex, traversal and hashing work. There is no public callback or detached +background generator. Independent calls can safely reuse immutable preparation. + +A successful abstract trace is complete. Its bytes share diagnosticBytes with +native diagnostics; exhaustion throws instead of returning a truncated certificate. +Trace hashing includes its actual 64-byte hex result. The G5 correction removed an +unmatched 32-byte refund without changing the algorithm, transport or default caps. +Platform-wide release qualification and controlled performance remain separate gates. diff --git a/Sources/JSONSchemaExamples/Documentation.docc/TypedConstruction.md b/Sources/JSONSchemaExamples/Documentation.docc/TypedConstruction.md new file mode 100644 index 0000000..0243a9a --- /dev/null +++ b/Sources/JSONSchemaExamples/Documentation.docc/TypedConstruction.md @@ -0,0 +1,93 @@ +# Conservative typed construction + +Construct a bounded proposal, then certify the complete original schema. + +```swift +import JSON +import JSONSchema +import JSONSchemaExamples + +let document = try JSONSchemaDocument(JSONParser().parse( + #"{"type":"integer","minimum":9007199254740993}"# +)) +let generator = try JSONSchemaExampleGenerator(document: document) +let result = try generator.generate(seed: 42) +precondition(result.validation.isValid) +``` + +The standalone public consumer's G3 mode runs this shape and all 24 foundational +must-generate fixtures with both profiles and four seeds. It independently +validates and roundtrips all 192 values, checks 90 witness expectations and keeps +two trivial unsatisfiability proofs separate. Its caller I/O/assertions are +qualification work, separate from each generator invocation's cumulative meter. + +## Exact scalars and containers + +Basic numeric lower/upper/open bounds and multipleOf use exact bounded decimal +steps and signed rounding. Integers larger than UInt64 remain exact. There is no +Double, Foundation Decimal or epsilon intermediary. Dense digit alignment can +exhaust a finite quota; this is not a general constraint solver. + +Strings count Unicode scalars. Demonstration can use immutable synthetic tokens; +known asserted email uses a fixed example.com address. Format metadata never +implicitly asserts a format or changes the declared type. Required keys are +preserved as exact scalar sequences, including simultaneous NFC/NFD spellings. +MinProperties can fill declared or synthetic keys. Each conjunction contributes +its own property/additional constraints, with no dictionary override or inferred +requiredness. Arrays preserve nesting, use positional prefix/items declarations +and bounded size. Uniqueness uses finite leaf variations and native certification. + +## References, composition and incomplete search + +Static reference targets come from the prepared compiler plan. Positive allOf +constraints remain separate conjunctions. AnyOf/oneOf declarations provide finite +alternative proposals, never branch-local certificates. Repeated DAG targets are +shared constraints; a same-instance ancestor reference cycle is unsupported. +Recursive containers can use an admitted empty base case. No independent URI +resolver, schema algebra, default repair or alternate validator is introduced. + +Construction uses an explicit frame stack and the same invocation meter through +staging, comparisons, exact arithmetic, false verdicts and final trace hashing. +Operational error/cancellation terminates the call. Returned logical accounting +is not process RSS or caller instruction accounting. Required defaults/examples +can be invalid; an independently constructed proposal may later pass while the +original metadata remains unchanged. + +## Supported construction subset + +| Declarations | Current proposal behavior | Boundary | +| --- | --- | --- | +| const, enum, default, examples | Exact declared candidates, arrays in declaration order | Defaults/examples can be invalid; no repair | +| type and unions | Fixed kind order and qualified intersections | No universal witness promise | +| minimum/maximum, exclusive bounds, multipleOf | Bounded exact decimal stepping/rounding | Several divisors or dense alignment can fail or exhaust limits | +| minLength/maxLength | Scalar-counted corpus or filler strings | Byte caps also apply; no implicit Unicode normalization | +| required/properties/minProperties/maxProperties | Exact required keys, declared or synthetic names | No general pattern/name/dependency key synthesis | +| prefixItems/items/minItems/maxItems/uniqueItems | Positional typed proposals and finite variations | Uniqueness can exhaust search | +| contains/minContains/maxContains | Minimum matching-position hints | Full native evaluation counts all matches and maxima | +| Anchored literal/range/exact-repeat patterns | Bounded ASCII proposals | No general regex inversion; native regex certifies | +| Static refs, allOf, anyOf, oneOf | Qualified targets, separate conjunctions and finite hints | No schema merging or branch-local certification | +| if/then/else, dependencies, not, unevaluated locations | Native certification of offered candidates | No general constructive solver | +| Dynamic references and cycles | Optional omission or finite base case can succeed | Reached dynamic scope/active same-instance cycles fail explicitly | + +The machine-readable Release1.6/Capabilities ledger classifies all 59 compiled +keywords; it describes construction hints, not changes to Draft 2020-12 evaluation. +A validator-supported keyword is not necessarily constructively supported. G3/G4 +fixture success applies to the pinned cases, not every schema using those keywords. + +## Preserve exact keys and requiredness + +```swift +let document = try JSONSchemaDocument(JSONParser().parse( + #"{"type":"object","required":["é","e\u0301"],"properties":{"é":{"const":9007199254740993},"e\u0301":{"const":null},"optional":{"default":42}}}"# +)) +let result = try JSONSchemaExampleGenerator(document: document).generate() +let object = result.value.objectValue +let exact = try JSONNumber(parsing: "9007199254740993") +precondition(object?.count == 2 && object?["é"]?.numberValue == exact) +precondition(object?["e\u{301}"] == .null && object?["optional"] == nil) +``` + +Both scalar-distinct keys survive. Null is present; the optional default is not +inserted. See for cumulative work and retention semantics. +G5 native lifecycle qualification is complete; final platform/integration/release +qualification remains G7/G8. diff --git a/Sources/JSONSchemaExamples/Documentation.docc/VerifiedCandidates.md b/Sources/JSONSchemaExamples/Documentation.docc/VerifiedCandidates.md new file mode 100644 index 0000000..f30b4ed --- /dev/null +++ b/Sources/JSONSchemaExamples/Documentation.docc/VerifiedCandidates.md @@ -0,0 +1,95 @@ +# Verified candidates + +Prepare an offline schema once and return exact JSON only after native validation succeeds. + +## Prepare and generate + +Add the optional JSONSchemaExamples product to your application target, then import +JSON, JSONSchema and JSONSchemaExamples explicitly. This product is in 1.6 development; +use the inspected feature checkout until a qualified release includes it. + +```swift +import JSON +import JSONSchema +import JSONSchemaExamples + +let document = try JSONSchemaDocument(JSONParser().parse( + #"{"const":9007199254740993}"# +)) +let prepared = try JSONSchemaExampleGenerator(document: document) +let result = try prepared.generate(seed: 42, profile: .minimal) +let text = try JSONWriter().writeString(result.value) +precondition(result.validation.isValid && text == "9007199254740993") +precondition(result.attempts == 1 && result.statistics[.evaluationWorkUnits] > 0) +``` + +``JSONSchemaExampleGenerator`` retains the admitted document, explicit registry, +compiled plan and policies. Each call owns independent mutable state. A successful +``JSONSchemaExampleResult`` contains its exact value, actual native verdict, +cumulative statistics and versioned provenance. Callers cannot initialize a +certificate through a public initializer. The existing JSON model and +JSONSchemaCompiler/JSONSchemaValidator remain the semantic authorities. + +## Select a definition and supply offline resources + +A document containing only `$defs` does not select an instance schema. Root the +requested definition explicitly. Retrieval URIs identify resources; they never +cause network access. Compilation uses only packaged meta-schemas and your registry. + +```swift +let parser = JSONParser() +let resource = try JSONSchemaDocument(parser.parse( + #"{"$defs":{"count":{"type":"integer","minimum":9007199254740993}}}"# +), retrievalURI: JSONSchemaURI("https://example.com/schemas/counts")) +let registry = try JSONSchemaRegistry(documents: [resource]) +let root = try JSONSchemaDocument(parser.parse( + ##"{"$ref":"https://example.com/schemas/counts#/$defs/count"}"## +)) +let prepared = try JSONSchemaExampleGenerator(document: root, registry: registry) +let result = try prepared.generate() +let plan = try JSONSchemaCompiler().compile(root, registry: registry) +let independentlyValid = try plan.validate(result.value).isValid +let exact = try JSONNumber(parsing: "9007199254740993") +let roundtrip = try JSONParser().parse(JSONWriter().write(result.value)) +precondition(independentlyValid && result.value.numberValue == exact) +precondition(roundtrip == result.value) +``` + +The final verdict always evaluates the original prepared root, including reference +siblings. Raw `$defs`, defaults, examples and unknown metadata are retained without +inserting fields, repairing candidates or inferring requiredness. Static targets +come from the compiler-qualified plan; construction does not implement another URI +resolver. Reached dynamic scope and active same-instance cycles can fail explicitly. + +## Handle incomplete search separately from errors + +A rejected candidate is an ordinary completed false verdict and consumes budget. +An exhausted search can still have a valid witness outside the offered candidates. +Only root false and an active root `not` of true/empty schema have trivial proofs. +No search exhaustion, reference cycle or operational error establishes unsatisfiability. + +```swift +let document = try JSONSchemaDocument(JSONParser().parse( + #"{"const":1,"type":"string"}"# +)) +let prepared = try JSONSchemaExampleGenerator(document: document) +var exhausted = false +do { + _ = try prepared.generate() +} catch JSONSchemaExampleFailure.searchExhausted(let attempts) { + precondition(attempts == 1) + exhausted = true +} +precondition(exhausted) +``` + +Other construction outcomes use ``JSONSchemaExampleFailure/unsupportedFeature(_:)``, +``JSONSchemaExampleFailure/provenUnsatisfiable(_:)`` or +``JSONSchemaExampleFailure/invalidConfiguration(_:)``. Native compilation/evaluation +errors, resource errors and CancellationError propagate separately; catch them only +when the application can handle them. A failure has no value or successful work report. + +See , , +and for the bounded construction and accounting contracts. +Every Swift block in these guides is synchronized with and executed by the standalone +Examples/SchemaExamplesConsumer package using public imports only. diff --git a/Sources/JSONSchemaExamples/JSONSchemaExampleCorpus.swift b/Sources/JSONSchemaExamples/JSONSchemaExampleCorpus.swift new file mode 100644 index 0000000..eb3b279 --- /dev/null +++ b/Sources/JSONSchemaExamples/JSONSchemaExampleCorpus.swift @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSON + +/// Immutable ordered synthetic candidates, admitted when a generator is prepared. +/// +/// Tokens never override schema semantics. The demonstration profile uses them +/// for true/empty schemas and conservative string proposals. Corpus order and +/// exact admitted contents contribute to deterministic identity. Preparation +/// checks all tokens with its finite policy; storing the corpus alone is unmetered. +public struct JSONSchemaExampleCorpus: Sendable { + /// Exact ordered values; order contributes to corpus identity and seeded choices. + public let values: [JSON] + /// Stores caller-authored values without admission or implicit numeric conversion. + /// - Parameter values: A nonempty corpus checked under preparation limits later. + public init(values: [JSON]) { self.values = values } + /// Version 1 synthetic corpus: null, false, zero, "example", empty array and empty object. + /// Materializes values for the caller's lifetime instead of retaining JSON payloads globally. + /// As with caller-authored corpora, preparation performs the checked admission later. + public static var synthetic: Self { + Self(values: [.null, .boolean(false), 0, .string("example"), .array([]), .object(.init(empty: ()))]) + } +} diff --git a/Sources/JSONSchemaExamples/JSONSchemaExampleGenerator.swift b/Sources/JSONSchemaExamples/JSONSchemaExampleGenerator.swift new file mode 100644 index 0000000..a31815b --- /dev/null +++ b/Sources/JSONSchemaExamples/JSONSchemaExampleGenerator.swift @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSON +import JSONSchema + +/// An immutable, offline source snapshot with invocation-owned candidate search. +/// +/// Declared hints and conservative typed construction share one cumulative ledger. +/// Every candidate is validated against the original compiled root. Dynamic scope +/// and difficult interdependent constraints can still fail explicitly. Preparation +/// has its own finite ledger; each generate call is independent and shares one +/// cumulative ledger through all candidate checks. See +/// and for supported construction and lifecycle boundaries. +public struct JSONSchemaExampleGenerator: Sendable { + /// The admitted source document, including any caller-supplied source map. + public let document: JSONSchemaDocument + /// The explicit immutable offline registry used during compilation and discovery. + public let registry: JSONSchemaRegistry + /// The frozen preparation, generation and evaluation policies. + public let options: JSONSchemaExampleOptions + /// The normalized source/registry snapshot digest; policies and corpus have separate identities. + public let sourceSnapshotDigest: String + /// The single preparation ledger, covering compilation, serialization and hashing. + public let preparationStatistics: JSONOperationStatistics + /// SHA-256 of the versioned, admitted ordered corpus transport. + public let corpusDigest: String + /// SHA-256 binding all generation, compilation and evaluation policies. + public let policyDigest: String + private let corpus: [JSON] + private let compiled: CompiledJSONSchema + + /// Compiles once and prepares a bounded source snapshot without online resolution. + /// - Parameters: + /// - document: The already admitted root schema. + /// - registry: Explicit immutable resources, including any custom meta-schemas. + /// - options: Finite preparation/search/evaluation policies retained unchanged. + /// - Throws: Generator configuration, native compilation/resource errors or cancellation. + public init(document: JSONSchemaDocument, registry: JSONSchemaRegistry = .empty, + options: JSONSchemaExampleOptions = .init()) throws { + try Task.checkCancellation() + guard options.maximumAttempts > 0, options.maximumAttempts < UInt64.max else { + throw JSONSchemaExampleFailure.invalidConfiguration(.attemptLimit) + } + guard !options.corpus.values.isEmpty else { + throw JSONSchemaExampleFailure.invalidConfiguration(.emptyCorpus) + } + try options.limits.validate(); try options.validationOptions.limits.validate() + var meter = try JSONWorkMeter(limits: options.compilationOptions.limits, domain: .schemaCompilation) + let compiled = try JSONSchemaCompiler(options: options.compilationOptions) + .compile(document, registry: registry, meter: &meter) + let digest = try SchemaExampleDigests.snapshot(document: document, registry: registry, meter: &meter) + let normalizedCorpus = try SchemaExampleExact.construct(.array(options.corpus.values), meter: &meter) + guard let tokens = normalizedCorpus.arrayValue else { throw JSONFailure(.configuration, code: .invalidOperation) } + corpus = tokens + corpusDigest = try SchemaExampleDigests.corpus(tokens, meter: &meter) + policyDigest = try SchemaExampleDigests.policy(options, meter: &meter) + self.document = document; self.registry = registry; self.options = options + self.compiled = compiled; sourceSnapshotDigest = digest + preparationStatistics = meter.statistics() + } + + /// Offers bounded exact candidates, returning only a completed native valid verdict. + /// + /// False candidate verdicts retain their work and diagnostic charges. An + /// operational error or cancellation ends the invocation without a candidate + /// or successful report. Reusing this prepared generator starts an independent + /// invocation; it does not continue an earlier call's remaining budget. + /// - Parameters: + /// - seed: SplitMix64 initial state for demonstration choices; always bound into the trace. + /// - profile: Stable minimal order or seeded demonstration candidate selection. + /// - Returns: The exact certified value and cumulative successful-invocation statistics. + /// - Throws: Explicit construction failure, native operational error or cancellation. + public func generate(seed: UInt64 = 0, profile: JSONSchemaExampleProfile = .minimal) throws -> JSONSchemaExampleResult { + let limits = try options.limits.intersection(options.validationOptions.limits) + var meter = try JSONWorkMeter(limits: limits, domain: .core) + return try generate(seed: seed, profile: profile, meter: &meter) + } + + // Internal qualification seam, also used by the public entry. The caller must + // supply the actual effective policy; no fresh meter or mutable callback is + // introduced inside search. Successful statistics describe certification, + // before ownership transfers to the caller and invocation reservations end. + func generate(seed: UInt64 = 0, profile: JSONSchemaExampleProfile = .minimal, + meter: inout JSONWorkMeter) throws -> JSONSchemaExampleResult { + try meter.checkpoint() + let retained = meter.used(.retainedBytes), annotations = meter.used(.annotationBytes) + let regex = meter.used(.regexRetainedBytes) + do { + guard meter.limits == (try options.limits.intersection(options.validationOptions.limits)), + meter.domain == .core else { throw JSONFailure(.configuration, code: .invalidOperation) } + let result = try search(seed: seed, profile: profile, meter: &meter) + try endScope(retained: retained, annotations: annotations, regex: regex, meter: &meter) + return result + } catch { + // Includes evaluator errors that do not originate in the meter and + // incomplete search. Cleanup is allowed after cancellation/exhaustion; + // it cannot refund work or clear the original terminal health. + meter.recordFailure(error) + try endScope(retained: retained, annotations: annotations, regex: regex, meter: &meter) + throw error + } + } + + private func endScope(retained: UInt64, annotations: UInt64, regex: UInt64, + meter: inout JSONWorkMeter) throws { + let (bytes, badBytes) = meter.used(.retainedBytes).subtractingReportingOverflow(retained) + let (annotationBytes, badAnnotations) = meter.used(.annotationBytes).subtractingReportingOverflow(annotations) + let (regexBytes, badRegex) = meter.used(.regexRetainedBytes).subtractingReportingOverflow(regex) + guard !badBytes, !badAnnotations, !badRegex else { throw JSONFailure(.configuration, code: .invalidOperation) } + try meter.releaseAnnotationState(annotationBytes) + try meter.releaseRegexState(regexBytes) + try meter.release(bytes) + } + + private func search(seed: UInt64, profile: JSONSchemaExampleProfile, + meter: inout JSONWorkMeter) throws -> JSONSchemaExampleResult { + var random = SchemaExampleRandom(seed: seed) + var trace = try SchemaExampleTrace(snapshot: sourceSnapshotDigest, corpus: corpusDigest, + policy: policyDigest, seed: seed, profile: profile, meter: &meter) + let root = compiled.exampleRoot + let declaration = try root.declaration(meter: &meter) + if declaration.raw.booleanValue == false { + throw JSONSchemaExampleFailure.provenUnsatisfiable(.falseSchema) + } + if declaration.vocabulary.applies(.not), + let negated = try declaration.raw.objectValue?.lookup("not", meter: &meter), + negated.booleanValue == true || negated.objectValue?.isEmpty == true { + throw JSONSchemaExampleFailure.provenUnsatisfiable(.notTrue) + } + try meter.retain(JSONLogicalWeights.frame) + var pending = [root], visited: [Int] = [], constructionSeeds: [JSONSchemaQualifiedNode] = [] + var attempts: UInt64 = 0 + while let node = pending.popLast() { + try meter.checkpoint(); try meter.work(); try meter.release(JSONLogicalWeights.frame) + var seen = false + for identity in visited { try meter.work(); if identity == node.identity { seen = true; break } } + if seen { continue } + try meter.charge(.schemaNodes) + try meter.retain(JSONLogicalWeights.indexSlot); visited.append(node.identity) + try trace.event(1, 0, 0, meter: &meter) + try meter.retain(JSONLogicalWeights.indexSlot); constructionSeeds.append(node) + let view = try node.declaration(meter: &meter) + for offset in 0..= traceBytes else { throw JSONFailure(.configuration, code: .invalidOperation) } + try meter.release(delta - traceBytes) + } + } + if attempts == 0 { throw JSONSchemaExampleFailure.unsupportedFeature(.candidateConstruction) } + throw JSONSchemaExampleFailure.searchExhausted(attempts: attempts) + } + + private func scan(_ values: [JSON], role: UInt64, seed: UInt64, profile: JSONSchemaExampleProfile, + attempts: inout UInt64, random: inout SchemaExampleRandom, + trace: inout SchemaExampleTrace, meter: inout JSONWorkMeter) throws -> JSONSchemaExampleResult? { + guard !values.isEmpty else { return nil } + var index = profile == .minimal ? 0 : Int(try random.uniform(upperBound: UInt64(values.count), trace: &trace, meter: &meter)) + for _ in values.indices { + try meter.work() + try trace.event(2, role, UInt64(index), meter: &meter) + if let result = try certify(values[index], seed: seed, profile: profile, attempts: &attempts, + random: &random, trace: &trace, meter: &meter) { return result } + // Avoid start + offset overflow on 32-bit WASI. + index = index == values.count - 1 ? 0 : index + 1 + } + return nil + } + + private func certify(_ value: JSON, seed: UInt64, profile: JSONSchemaExampleProfile, + attempts: inout UInt64, random: inout SchemaExampleRandom, + trace: inout SchemaExampleTrace, meter: inout JSONWorkMeter) throws -> JSONSchemaExampleResult? { + try meter.checkpoint() + guard attempts < options.maximumAttempts else { throw JSONSchemaExampleFailure.searchExhausted(attempts: attempts) } + // The configured ceiling is strictly below UInt64.max, so this cannot wrap. + attempts += 1 + try meter.resetValueBounds() + let retainedBefore = meter.used(.retainedBytes) + let annotationBefore = meter.used(.annotationBytes) + let value = try SchemaExampleExact.construct(value, meter: &meter) + try meter.resetValueBounds() + try meter.operationDomain(.schemaEvaluation) + let verdict = try JSONSchemaValidator(options: options.validationOptions).validate(value, against: compiled, meter: &meter) + try meter.operationDomain(.core) + guard verdict.isValid else { + // Nothing from this verdict escapes. Release only this attempt's live + // ownership; work, diagnostics and lifetime admission counts stay charged. + let (retained, badRetained) = meter.used(.retainedBytes).subtractingReportingOverflow(retainedBefore) + let (annotations, badAnnotations) = meter.used(.annotationBytes).subtractingReportingOverflow(annotationBefore) + guard !badRetained, !badAnnotations else { throw JSONFailure(.configuration, code: .invalidOperation) } + try meter.releaseAnnotationState(annotations) + try meter.release(retained) + try trace.event(4, attempts, 0, meter: &meter) + return nil + } + try trace.event(4, attempts, 1, meter: &meter) + try trace.event(5, attempts, random.draws, meter: &meter) + let events = trace.events + let traceDigest = try trace.digest(meter: &meter) + try meter.retain(512); try meter.checkpoint() + return .init(value: value, validation: verdict, seed: seed, profile: profile, + generatorVersion: SchemaExampleVersion.generator, sourceSnapshotDigest: sourceSnapshotDigest, + schemaDigestAlgorithm: SchemaExampleVersion.snapshot, + corpusDigestAlgorithm: SchemaExampleVersion.corpus, corpusDigest: corpusDigest, + policyDigestAlgorithm: SchemaExampleVersion.policy, policyDigest: policyDigest, + rngAlgorithm: SchemaExampleVersion.rng, randomDraws: random.draws, + traceDigestAlgorithm: SchemaExampleVersion.trace, traceDigest: traceDigest, traceEventCount: events, + validationOptions: options.validationOptions, compilationOptions: options.compilationOptions, + attempts: attempts, statistics: meter.statistics()) + } +} diff --git a/Sources/JSONSchemaExamples/JSONSchemaExampleOptions.swift b/Sources/JSONSchemaExamples/JSONSchemaExampleOptions.swift new file mode 100644 index 0000000..ebc6165 --- /dev/null +++ b/Sources/JSONSchemaExamples/JSONSchemaExampleOptions.swift @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSON +import JSONSchema + +/// Finite preparation, search and validation policies for an offline generator. +public struct JSONSchemaExampleOptions: Sendable { + /// Generation capacities; their intersection with validation limits governs each invocation. + public var limits: JSONLimits + /// The positive finite ceiling on completed candidate validations, including invalid candidates. + /// Defaults to 16; zero and UInt64.max are rejected at preparation. It is not + /// a completeness guarantee: unsupported construction may stop before this ceiling. + public var maximumAttempts: UInt64 + /// Immutable ordered candidates admitted and hashed during preparation. + public var corpus: JSONSchemaExampleCorpus + /// One compilation/snapshot/corpus/policy preparation budget, separate from generation. + /// Its format-assertion setting remains distinct from evaluation policy. + public var compilationOptions: JSONSchemaOptions + /// Unchanged evaluator policy; required vocabulary assertions remain authoritative. + /// Its limits intersect generation limits, while output, diagnostics, annotations + /// and explicit format assertion are forwarded unchanged to native validation. + public var validationOptions: JSONSchemaOptions + + /// Creates policies checked when the generator is prepared. + /// - Parameters: + /// - limits: Finite generation capacities and cumulative quotas. + /// - maximumAttempts: A positive value strictly below UInt64.max. + /// - corpus: Exact synthetic tokens; never authoritative over schema constraints. + /// - compilationOptions: Offline compilation and preparation limits/policies. + /// - validationOptions: Instance evaluation limits and diagnostic/format policies. + public init(limits: JSONLimits = .balanced, maximumAttempts: UInt64 = 16, + corpus: JSONSchemaExampleCorpus = .synthetic, + compilationOptions: JSONSchemaOptions = .init(), + validationOptions: JSONSchemaOptions = .init()) { + self.limits = limits; self.maximumAttempts = maximumAttempts; self.corpus = corpus + self.compilationOptions = compilationOptions; self.validationOptions = validationOptions + } +} + +/// A candidate preference, without a guarantee of globally minimal output. +public enum JSONSchemaExampleProfile: Sendable { + /// Prefer stable declared candidates and small conservative typed values. + case minimal + /// Seeded declared-candidate scans and synthetic tokens for admitted typed strings. + case demonstration +} + +/// A construction feature not implemented by this algorithm version. +public enum JSONSchemaExampleUnsupportedFeature: Sendable, Equatable { + /// No supported declaration supplied a candidate; this does not establish unsatisfiability. + case candidateConstruction + /// Construction would need evaluation-scope-aware dynamic reference navigation. + case dynamicReference + /// Construction revisited an active same-instance ancestor without progress. + case cyclicReference +} + +/// A sound trivial root-schema proof, distinct from incomplete search. +public enum JSONSchemaUnsatisfiableProof: Sendable, Equatable { + /// The original root is the Boolean false schema. + case falseSchema + /// The active root applicator negates true or an empty object schema. + case notTrue +} + +/// An invalid generator-specific configuration, without raw schema content. +public enum JSONSchemaExampleConfigurationIssue: Sendable, Equatable { + /// maximumAttempts was zero or UInt64.max. + case attemptLimit + /// The corpus had no candidates. + case emptyCorpus +} + +/// Bounded construction failures; core operational errors and cancellation propagate separately. +public enum JSONSchemaExampleFailure: Error, Sendable, Equatable { + /// A construction feature is unsupported, without a validity verdict. + /// - Parameter feature: The stable unsupported-feature code. + case unsupportedFeature(_ feature: JSONSchemaExampleUnsupportedFeature) + /// All offered candidates failed validation or the attempt ceiling was reached. + /// - Parameter attempts: The number of completed candidate validations. + case searchExhausted(attempts: UInt64) + /// A reviewed trivial proof establishes that the original root cannot accept any instance. + /// - Parameter proof: The sound proof kind; never inferred from exhaustion. + case provenUnsatisfiable(_ proof: JSONSchemaUnsatisfiableProof) + /// Generator configuration was invalid before compilation or search. + /// - Parameter issue: The stable configuration code. + case invalidConfiguration(_ issue: JSONSchemaExampleConfigurationIssue) +} diff --git a/Sources/JSONSchemaExamples/JSONSchemaExampleResult.swift b/Sources/JSONSchemaExamples/JSONSchemaExampleResult.swift new file mode 100644 index 0000000..4bd7108 --- /dev/null +++ b/Sources/JSONSchemaExamples/JSONSchemaExampleResult.swift @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSON +import JSONSchema + +/// A candidate certified by completed native validation against the prepared root. +/// +/// Only the generator initializes results. The current algorithm offers declared hints, static +/// references and conservative typed construction, not arbitrary satisfiability decisions. +public struct JSONSchemaExampleResult: Sendable { + /// The exact candidate that received the attached valid verdict. + public let value: JSON + /// The native validator's completed valid result, including its actual diagnostics/annotations. + public let validation: JSONValidationResult + /// The caller's SplitMix64 seed, bound into the deterministic trace. + public let seed: UInt64 + /// The requested candidate preference. + public let profile: JSONSchemaExampleProfile + /// The versioned candidate algorithm, currently s3-candidates-v4. + public let generatorVersion: String + /// SHA-256 of the documented normalized source/registry transport, not original text or JCS. + public let sourceSnapshotDigest: String + /// The transport and hashing identity, currently swift-json-schema-snapshot-v1. + public let schemaDigestAlgorithm: String + /// The versioned ordered corpus transport identity. + public let corpusDigestAlgorithm: String + /// SHA-256 of the prepared exact corpus, including token order. + public let corpusDigest: String + /// The versioned all-caps policy transport identity. + public let policyDigestAlgorithm: String + /// SHA-256 binding generation, compilation and validation configuration. + public let policyDigest: String + /// The random algorithm identity, splitmix64-v1, even when no draws were needed. + public let rngAlgorithm: String + /// All raw draws, including those rejected by unbiased bounded selection. + public let randomDraws: UInt64 + /// The versioned complete decision transport identity. + public let traceDigestAlgorithm: String + /// SHA-256 of every bounded abstract decision through the final valid verdict. + public let traceDigest: String + /// The complete event count; trace exhaustion throws rather than truncating. + public let traceEventCount: UInt64 + /// The exact instance evaluation policy used for certification. + public let validationOptions: JSONSchemaOptions + /// The separate compilation policy that helped establish the prepared plan's assertions. + public let compilationOptions: JSONSchemaOptions + /// The total completed candidate validations, including preceding invalid verdicts. + public let attempts: UInt64 + /// The shared invocation ledger, including all completed candidate checks. + /// + /// evaluationWorkUnits is included in workUnits. Preparation is separate. + /// Discarded validation ownership is released without refunding work. Retained bytes + /// snapshot certification, including conservative staging reservations; ownership + /// transfers to the caller after that snapshot. Logical bytes are not heap size or RSS. + public let statistics: JSONOperationStatistics +} diff --git a/Sources/JSONSchemaExamples/SchemaExampleConstraints.swift b/Sources/JSONSchemaExamples/SchemaExampleConstraints.swift new file mode 100644 index 0000000..c432f48 --- /dev/null +++ b/Sources/JSONSchemaExamples/SchemaExampleConstraints.swift @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSON +import JSONSchema + +struct SchemaExampleConstraint { + let node: JSONSchemaQualifiedNode + let view: JSONSchemaQualifiedNode.Declaration + func value(_ key: JSONSchemaPlan.Keyword, meter: inout JSONWorkMeter) throws -> JSON? { + guard view.vocabulary.applies(key) else { return nil } + return try view.raw.objectValue?.lookup(key.rawValue, meter: &meter) + } + func children(_ keyword: JSONSchemaPlan.Keyword, token: String? = nil, + meter: inout JSONWorkMeter) throws -> [JSONSchemaQualifiedNode] { + guard view.vocabulary.applies(keyword) else { return [] } + var result: [JSONSchemaQualifiedNode] = [] + for i in 0.. [SchemaExampleConstraint] { + var pending: [Step] = [], active: [Int] = [], seen: [Int] = [], result: [SchemaExampleConstraint] = [] + func push(_ step: Step, meter: inout JSONWorkMeter) throws { + try meter.require(.evaluationFrames, UInt64(pending.count) + 1) + try meter.retain(JSONLogicalWeights.frame); pending.append(step) + } + for node in nodes.reversed() { try push(.enter(node), meter: &meter) } + while let step = pending.popLast() { + try meter.work(); try meter.release(JSONLogicalWeights.frame) + switch step { + case .exit(let identity): + guard active.last == identity else { throw JSONFailure(.configuration, code: .invalidOperation) } + active.removeLast(); try meter.release(JSONLogicalWeights.indexSlot) + case .enter(let node): + for id in active { + try meter.work() + if id == node.identity { throw JSONSchemaExampleFailure.unsupportedFeature(.cyclicReference) } + } + var visited = false + for id in seen { try meter.work(); if id == node.identity { visited = true; break } } + if visited { continue } + try meter.charge(.schemaNodes); try meter.retain(JSONLogicalWeights.indexSlot * 2 + 64) + seen.append(node.identity); active.append(node.identity) + let view = try node.declaration(meter: &meter), constraint = SchemaExampleConstraint(node: node, view: view) + result.append(constraint) + try push(.exit(node.identity), meter: &meter) + // AllOf is conjunction; no alternative branch or conditional is flattened. + let children = try constraint.children(.allOf, meter: &meter) + for child in children.reversed() { try push(.enter(child), meter: &meter) } + for kind in SchemaExampleVersion.referencePushOrder { + for i in 0.. String { + // UInt64 decimal formatting is bounded by twenty ASCII digits. It must + // remain positional text (index 10 is "10", not normalized JSON "1e1"). + try meter.retain(40) + for _ in 0..<20 { try meter.work(subsystem: .numericWorkUnits) } + return String(value) + } + static func appendNodes(_ input: [JSONSchemaQualifiedNode], to output: inout [JSONSchemaQualifiedNode], + meter: inout JSONWorkMeter) throws { + for node in input { try meter.work(); try meter.retain(JSONLogicalWeights.indexSlot); output.append(node) } + } + static func propertyNodes(_ constraints: [SchemaExampleConstraint], key: JSONKey, + meter: inout JSONWorkMeter) throws -> [JSONSchemaQualifiedNode] { + var result: [JSONSchemaQualifiedNode] = [] + for c in constraints { + let declared = try c.value(.properties, meter: &meter)?.objectValue?.lookup(key.string, meter: &meter) + let nodes = try c.children(declared == nil ? .additionalProperties : .properties, + token: declared == nil ? nil : key.string, meter: &meter) + try appendNodes(nodes, to: &result, meter: &meter) + } + return result + } + static func textLess(_ a: String, _ b: String, meter: inout JSONWorkMeter) throws -> Bool { + var a = a.utf8.makeIterator(), b = b.utf8.makeIterator() + while true { + try meter.work(); let x = a.next(), y = b.next() + if x != y { if let x, let y { return x < y }; return x == nil } + if x == nil { return false } + } + } + static func insert(_ spelling: String, into keys: inout [JSONKey], meter: inout JSONWorkMeter) throws { + var position = keys.count + for (i, key) in keys.enumerated() { + if try JSONComparison.textEquals(key.string, spelling, meter: &meter) { return } + if try textLess(spelling, key.string, meter: &meter) { position = i; break } + } + try meter.require(.objectMembers, UInt64(keys.count) + 1) + try JSONAdmission.text(spelling, key: true, meter: &meter, owned: true) + let key = try JSONKey(spelling, meter: &meter) + try meter.retain(JSONLogicalWeights.indexSlot) + for _ in position.. [JSONKey]? { + guard let bounds = try SchemaExamplePrimitives.size(constraints, minimum: .minProperties, maximum: .maxProperties, + capacity: .objectMembers, meter: &meter) else { return nil } + var keys: [JSONKey] = [] + for c in constraints { + if let required = try c.value(.required, meter: &meter)?.arrayValue { + for value in required { + try meter.work() + guard let spelling = value.stringValue else { throw JSONFailure(.configuration, code: .invalidOperation) } + try insert(spelling, into: &keys, meter: &meter) + } + } + } + if UInt64(keys.count) > bounds.maximum { return nil } + var declared: [JSONKey] = [] + for c in constraints { + if let properties = try c.value(.properties, meter: &meter)?.objectValue { + for member in properties { + try meter.work() + if member.value.booleanValue != false { try insert(member.key.string, into: &declared, meter: &meter) } + } + } + } + for key in declared { + if UInt64(keys.count) >= bounds.minimum { break } + try insert(key.string, into: &keys, meter: &meter) + } + var ordinal: UInt64 = 0 + while UInt64(keys.count) < bounds.minimum { + try meter.work() + let suffix = try integerText(ordinal, meter: &meter) + try meter.retain(UInt64(suffix.utf8.count) + 1) + for _ in suffix.utf8 { try meter.work() } + try insert("p" + suffix, into: &keys, meter: &meter) + ordinal = try meter.adding(.workUnits, ordinal, 1) + } + return keys + } + static func typeMask(_ constraints: [SchemaExampleConstraint], meter: inout JSONWorkMeter) throws -> UInt8 { + var mask: UInt8 = 127 + for c in constraints { + if c.view.raw.booleanValue == false { return 0 } + guard let type = try c.value(.type, meter: &meter) else { continue } + let values: [JSON] + if type.stringValue != nil { try meter.retain(JSONLogicalWeights.arraySlot); values = [type] } + else if let array = type.arrayValue { values = array } + else { throw JSONFailure(.configuration, code: .invalidOperation) } + var accepted: UInt8 = 0 + for value in values { + try meter.work() + guard let name = value.stringValue else { throw JSONFailure(.configuration, code: .invalidOperation) } + // Only the compiler's seven ASCII type names can reach this mapping. + for (spelling, bits) in typeNames where try JSONComparison.textEquals(name, spelling, meter: &meter) { accepted |= bits } + } + mask &= accepted + } + return mask + } + static let typeNames: [(String, UInt8)] = [("null",1),("boolean",2),("integer",4),("number",12),("string",16),("array",32),("object",64)] + static let hints: [JSONSchemaPlan.Keyword] = [.type,.minimum,.maximum,.exclusiveMinimum,.exclusiveMaximum,.multipleOf,.minLength,.maxLength,.pattern,.format,.required,.properties,.minProperties,.maxProperties,.items,.prefixItems,.minItems,.maxItems,.contains] + static func containsMinimum(_ c: SchemaExampleConstraint, meter: inout JSONWorkMeter) throws -> UInt64? { + guard try c.value(.contains, meter: &meter) != nil else { return nil } + if let number = try c.value(.minContains, meter: &meter)?.numberValue { + return try number.integer(as: UInt64.self, meter: &meter) + } + return 1 + } + static func construct(nodes: [JSONSchemaQualifiedNode], variation: UInt64, + profile: JSONSchemaExampleProfile, corpus: [JSON], assertion: Bool, + random: inout SchemaExampleRandom, trace: inout SchemaExampleTrace, + meter: inout JSONWorkMeter) throws -> JSON? { + var frames: [SchemaExampleFrame] = [], completed: JSON? + func push(_ frame: SchemaExampleFrame, meter: inout JSONWorkMeter) throws { + try meter.require(.evaluationFrames, UInt64(frames.count) + 1) + try meter.retain(JSONLogicalWeights.frame); frames.append(frame) + } + try push(.value(nodes, 0, variation), meter: &meter) + while let frame = frames.popLast() { + try meter.work(); try meter.release(JSONLogicalWeights.frame) + switch frame { + case .value(let nodes, let depth, let ordinal): + try trace.event(6, depth, ordinal, meter: &meter) + let constraints = try SchemaExampleConjunction.gather(nodes, meter: &meter) + var declared: JSON?, hasHint = nodes.isEmpty + for c in constraints { + if c.view.raw.booleanValue == false { return nil } + if let value = try c.value(.const, meter: &meter) { declared = value; break } + if let values = try c.value(.enum, meter: &meter)?.arrayValue, !values.isEmpty { + declared = values[Int(ordinal % UInt64(values.count))]; break + } + for hint in hints { if try c.value(hint, meter: &meter) != nil { hasHint = true; break } } + if c.view.raw.booleanValue == true || c.view.raw.objectValue?.isEmpty == true { hasHint = true } + } + // Whole-instance const/enum candidates were already tried by discovery. + if depth == 0 && declared != nil { return nil } + if let declared { + try JSONAdmission.value(declared, meter: &meter, owned: true, depth: depth) + completed = declared; continue + } + guard hasHint else { return nil } + try meter.charge(.nodes); try meter.retain(JSONLogicalWeights.node) + let mask = try typeMask(constraints, meter: &meter) + var kinds: [UInt8] = [] + for kind: UInt8 in [1,2,4,16,32,64] { + if mask & kind != 0 { try meter.retain(1); kinds.append(kind) } + } + guard !kinds.isEmpty else { return nil } + let kind = kinds[Int(ordinal % UInt64(kinds.count))] + switch kind { + case 1: completed = .null + case 2: completed = .boolean(ordinal % 2 != 0) + case 4: + guard let number = try SchemaExamplePrimitives.number(constraints, integral: mask & 8 == 0, variation: ordinal, meter: &meter) else { return nil } + completed = .number(number) + case 16: + guard let text = try SchemaExamplePrimitives.string(constraints, variation: ordinal, profile: profile, corpus: corpus, + assertion: assertion, random: &random, trace: &trace, meter: &meter) else { return nil } + completed = .string(text) + case 32: + let container = try meter.adding(.maxDepth, depth, 1) + guard let bounds = try SchemaExamplePrimitives.size(constraints, minimum: .minItems, maximum: .maxItems, + capacity: .arrayItems, meter: &meter) else { return nil } + var unique = false, count = bounds.minimum + for c in constraints { + if try c.value(.uniqueItems, meter: &meter)?.booleanValue == true { unique = true } + if let minimum = try containsMinimum(c, meter: &meter) { + try meter.require(.arrayItems, minimum) + if let maximum = try c.value(.maxContains, meter: &meter)?.numberValue, + try maximum.integer(as: UInt64.self, meter: &meter) < minimum { return nil } + count = max(count, minimum) + } + } + guard count <= bounds.maximum else { return nil } + try push(.array(.init(constraints: constraints, count: count, depth: container, variation: ordinal, unique: unique)), meter: &meter) + default: + let container = try meter.adding(.maxDepth, depth, 1) + guard let keys = try objectKeys(constraints, meter: &meter) else { return nil } + try push(.object(.init(constraints: constraints, keys: keys, depth: container, variation: ordinal)), meter: &meter) + } + case .object(var state): + if state.awaiting { + guard let value = completed else { throw JSONFailure(.configuration, code: .invalidOperation) } + try meter.retain(JSONLogicalWeights.member) + state.members.append(.init(state.keys[state.position - 1], value: value)); completed = nil; state.awaiting = false + } + if state.position == state.keys.count { + completed = .object(try JSONObject.indexed(admitted: state.members, meter: &meter)); continue + } + let nodes = try propertyNodes(state.constraints, key: state.keys[state.position], meter: &meter) + state.position += 1; state.awaiting = true + let depth = state.depth, ordinal = state.variation + try push(.object(state), meter: &meter); try push(.value(nodes, depth, ordinal), meter: &meter) + case .array(var state): + if state.awaiting { + guard let value = completed else { throw JSONFailure(.configuration, code: .invalidOperation) } + try meter.retain(JSONLogicalWeights.arraySlot); state.values.append(value); completed = nil; state.awaiting = false + } + let index = UInt64(state.values.count) + if index == state.count { completed = .array(state.values); continue } + var nodes: [JSONSchemaQualifiedNode] = [] + let token = try integerText(index, meter: &meter) + for c in state.constraints { + let prefix = try c.value(.prefixItems, meter: &meter)?.arrayValue + let kind: JSONSchemaPlan.Keyword = index < UInt64(prefix?.count ?? 0) ? .prefixItems : .items + try appendNodes(c.children(kind, token: kind == .prefixItems ? token : nil, meter: &meter), to: &nodes, meter: &meter) + if let minimum = try containsMinimum(c, meter: &meter), index < minimum { + try appendNodes(c.children(.contains, meter: &meter), to: &nodes, meter: &meter) + } + } + let ordinal = try meter.adding(.workUnits, state.variation, state.unique ? index : 0) + let depth = state.depth; state.awaiting = true + try push(.array(state), meter: &meter); try push(.value(nodes, depth, ordinal), meter: &meter) + } + } + return completed + } +} diff --git a/Sources/JSONSchemaExamples/SchemaExampleDecimal.swift b/Sources/JSONSchemaExamples/SchemaExampleDecimal.swift new file mode 100644 index 0000000..299ad4a --- /dev/null +++ b/Sources/JSONSchemaExamples/SchemaExampleDecimal.swift @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSON + +// Dense scratch is deliberately finite. Exponents stay sparse until an exact +// quotient needs alignment, which must fit numberDigits before zero expansion. +enum SchemaExampleDecimal { + struct Integer { let negative: Bool; let digits: [UInt8] } + static func tick(_ meter: inout JSONWorkMeter) throws { try meter.work(subsystem: .numericWorkUnits) } + static func append(_ digit: UInt8, to digits: inout [UInt8], meter: inout JSONWorkMeter) throws { + try tick(&meter); try meter.require(.numberDigits, UInt64(digits.count) + 1) + try meter.retain(1); digits.append(digit) + } + static func zero(_ digits: [UInt8]) -> Bool { digits.count == 1 && digits[0] == 0 } + static func trim(_ digits: [UInt8], meter: inout JSONWorkMeter) throws -> [UInt8] { + var start = 0 + while start < digits.count - 1 && digits[start] == 0 { try tick(&meter); start += 1 } + var result: [UInt8] = [] + for digit in digits[start...] { try append(digit, to: &result, meter: &meter) } + return result + } + static func compare(_ a: [UInt8], _ b: [UInt8], meter: inout JSONWorkMeter) throws -> Int { + try tick(&meter) + if a.count != b.count { return a.count < b.count ? -1 : 1 } + for i in a.indices { try tick(&meter); if a[i] != b[i] { return a[i] < b[i] ? -1 : 1 } } + return 0 + } + static func subtract(_ a: [UInt8], _ b: [UInt8], meter: inout JSONWorkMeter) throws -> [UInt8] { + var reversed: [UInt8] = [], borrow = 0 + for offset in 0.. [UInt8] { + var reversed: [UInt8] = [], carry = 0 + for offset in 0.. Integer { + var reversed: [UInt8] = [], remainder = amount + repeat { try append(UInt8(remainder % 10), to: &reversed, meter: &meter); remainder /= 10 } while remainder != 0 + var digits: [UInt8] = [] + for d in reversed.reversed() { try append(d, to: &digits, meter: &meter) } + if value.negative == negative { return .init(negative: negative, digits: try add(value.digits, digits, meter: &meter)) } + let order = try compare(value.digits, digits, meter: &meter) + if order >= 0 { return .init(negative: value.negative, digits: try subtract(value.digits, digits, meter: &meter)) } + return .init(negative: negative, digits: try subtract(digits, value.digits, meter: &meter)) + } + static func aligned(_ c: DecimalComponents, exponent: Int64, meter: inout JSONWorkMeter) throws -> [UInt8] { + if zero(c.digits) { try meter.retain(1); return [0] } + let (shift, overflow) = c.exponent.subtractingReportingOverflow(exponent) + guard !overflow, shift >= 0 else { throw JSONFailure(.resource, code: .counterOverflow) } + _ = try meter.adding(.numberDigits, UInt64(c.digits.count), UInt64(shift)) + var result: [UInt8] = [] + for d in c.digits { try append(d, to: &result, meter: &meter) } + for _ in 0.. (quotient: [UInt8], remainder: Bool) { + guard !zero(b) else { throw JSONFailure(.configuration, code: .invalidNumber) } + try meter.retain(1) + var remainder: [UInt8] = [0], quotient: [UInt8] = [] + for digit in a { + try tick(&meter) + if zero(remainder) { remainder[0] = digit } else { try append(digit, to: &remainder, meter: &meter) } + var q: UInt8 = 0 + while try compare(remainder, b, meter: &meter) >= 0 { + remainder = try subtract(remainder, b, meter: &meter); q += 1 + } + guard q <= 9 else { throw JSONFailure(.configuration, code: .invalidOperation) } + try append(q, to: "ient, meter: &meter) + } + return (try trim(quotient, meter: &meter), !zero(remainder)) + } + static func multiply(_ a: [UInt8], _ b: [UInt8], meter: inout JSONWorkMeter) throws -> [UInt8] { + let count = try meter.adding(.retainedBytes, UInt64(a.count), UInt64(b.count)) + try meter.retain(count) + for _ in 0.. JSONNumber { + var digits = try trim(digits, meter: &meter), exponent = exponent + while digits.count > 1 && digits.last == 0 { + try tick(&meter); digits.removeLast() + let (next, overflow) = exponent.addingReportingOverflow(1) + guard !overflow else { throw JSONFailure(.resource, code: .counterOverflow) }; exponent = next + } + try meter.require(.absoluteExponent, exponent.magnitude) + return JSONNumber(components: .init(negative: negative && !zero(digits), digits: digits, exponent: zero(digits) ? 0 : exponent)) + } + static func integralStep(_ step: JSONNumber, meter: inout JSONWorkMeter) throws -> JSONNumber { + let c = step.components + if c.exponent >= 0 { return step } + var digits = c.digits + for factor in [2, 5] { + for _ in 0.. JSONNumber { + let a = bound.components, b = step.components, common = min(a.exponent, b.exponent) + // Preserve sparse, already-aligned endpoints without materializing their quotient. + if variation == 0, !open, try bound.isMultiple(of: step, meter: &meter) { return bound } + let dividend = try aligned(a, exponent: common, meter: &meter) + let divisor = try aligned(b, exponent: common, meter: &meter) + let division = try divide(dividend, by: divisor, meter: &meter) + var integer = Integer(negative: a.negative, digits: division.quotient) + if division.remainder && up != a.negative { + integer = try offset(integer, by: 1, negative: a.negative, meter: &meter) + } + if !division.remainder && open { integer = try offset(integer, by: 1, negative: !up, meter: &meter) } + if variation > 0 { integer = try offset(integer, by: variation, negative: !up, meter: &meter) } + let product = try multiply(integer.digits, b.digits, meter: &meter) + return try number(product, negative: integer.negative, exponent: b.exponent, meter: &meter) + } +} diff --git a/Sources/JSONSchemaExamples/SchemaExampleDeterminism.swift b/Sources/JSONSchemaExamples/SchemaExampleDeterminism.swift new file mode 100644 index 0000000..2029f18 --- /dev/null +++ b/Sources/JSONSchemaExamples/SchemaExampleDeterminism.swift @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSON +import JSONSchema + +enum SchemaExampleVersion { + static let generator = "s3-candidates-v4" + static let rng = "splitmix64-v1" + static let snapshot = "swift-json-schema-snapshot-v1" + static let corpus = "swift-json-example-corpus-v1" + static let policy = "swift-json-example-policy-v1" + static let trace = "swift-json-example-trace-v2" + static let referencePushOrder: [JSONSchemaPlan.Keyword] = [.ref, .dynamicRef] + static let keywords: [(String, JSONSchemaPlan.Keyword)] = [("const", .const), ("default", .default), ("enum", .enum), ("examples", .examples)] +} + +// No Sendable conformance: mutable state belongs to one synchronous invocation. +struct SchemaExampleRandom: ~Copyable { + private var state: UInt64 + private(set) var draws: UInt64 = 0 + init(seed: UInt64) { state = seed } + + mutating func next(meter: inout JSONWorkMeter) throws -> UInt64 { + for _ in 0..<10 { try meter.work() } + draws = try meter.adding(.workUnits, draws, 1) + state &+= 0x9E3779B97F4A7C15 + var z = state + z = (z ^ (z >> 30)) &* 0xBF58476D1CE4E5B9 + z = (z ^ (z >> 27)) &* 0x94D049BB133111EB + return z ^ (z >> 31) + } + + mutating func uniform(upperBound: UInt64, trace: inout SchemaExampleTrace, + meter: inout JSONWorkMeter) throws -> UInt64 { + try meter.checkpoint() + guard upperBound > 0 else { + let failure = JSONFailure(.configuration, code: .invalidOperation) + meter.recordFailure(failure); throw failure + } + for _ in 0..<3 { try meter.work() } + let threshold = (UInt64(0) &- upperBound) % upperBound + while true { + let candidate = try next(meter: &meter) + try trace.event(3, candidate, upperBound, meter: &meter) + for _ in 0..<2 { try meter.work() } + if candidate >= threshold { return candidate % upperBound } + } + } +} + +// A finite transport buffer; trace bytes share the native diagnostic byte quota. +struct SchemaExampleTransport: ~Copyable { + private(set) var bytes: [UInt8] = [] + let isTrace: Bool + init(header: String, isTrace: Bool = false, meter: inout JSONWorkMeter) throws { + self.isTrace = isTrace + for byte in header.utf8 { try append(byte, meter: &meter) } + } + mutating func append(_ byte: UInt8, meter: inout JSONWorkMeter) throws { + try meter.work() + try meter.require(.inputBytes, UInt64(bytes.count) + 1) + if isTrace { try meter.charge(.diagnosticBytes) } + try meter.retain(1); bytes.append(byte) + } + mutating func integer(_ value: UInt64, meter: inout JSONWorkMeter) throws { + for shift in stride(from: 56, through: 0, by: -8) { + try append(UInt8(truncatingIfNeeded: value >> shift), meter: &meter) + } + } + mutating func field(_ value: C, meter: inout JSONWorkMeter) throws where C.Element == UInt8 { + var length: UInt64 = 0 + for _ in value { try meter.work(); length = try meter.adding(.inputBytes, length, 1) } + try integer(length, meter: &meter) + for byte in value { try append(byte, meter: &meter) } + } + mutating func digest(meter: inout JSONWorkMeter) throws -> String { + let hash = try JSONSHA256Core.hexadecimal(bytes, accounting: &meter) + try meter.release(UInt64(bytes.count)) + bytes = [] + return hash + } +} + +struct SchemaExampleTrace: ~Copyable { + private var transport: SchemaExampleTransport + private(set) var events: UInt64 = 0 + private var finished = false + var retainedByteCount: UInt64 { UInt64(transport.bytes.count) } + init(snapshot: String, corpus: String, policy: String, seed: UInt64, + profile: JSONSchemaExampleProfile, meter: inout JSONWorkMeter) throws { + var transport = try SchemaExampleTransport(header: "SwiftJSON-example-trace-v2\n", isTrace: true, meter: &meter) + try meter.retain(JSONLogicalWeights.arraySlot * 4) + for text in [SchemaExampleVersion.generator, snapshot, corpus, policy] { + try transport.field(text.utf8, meter: &meter) + } + try meter.release(JSONLogicalWeights.arraySlot * 4) + try transport.integer(seed, meter: &meter) + try transport.append(profile == .minimal ? 0 : 1, meter: &meter) + self.transport = consume transport + } + mutating func event(_ code: UInt8, _ a: UInt64, _ b: UInt64, meter: inout JSONWorkMeter) throws { + guard !finished else { + let failure = JSONFailure(.configuration, code: .invalidOperation) + meter.recordFailure(failure); throw failure + } + try transport.append(code, meter: &meter) + try transport.integer(a, meter: &meter); try transport.integer(b, meter: &meter) + events = try meter.adding(.workUnits, events, 1) + } + mutating func digest(meter: inout JSONWorkMeter) throws -> String { + guard !finished else { + let failure = JSONFailure(.configuration, code: .invalidOperation) + meter.recordFailure(failure); throw failure + } + finished = true + // The transport owns exactly its bytes. There is no array-slot + // reservation here to refund; SHA-256 retains its 64-byte hex result. + return try transport.digest(meter: &meter) + } +} + +enum SchemaExampleExact { + static func bytes(_ value: JSON, meter: inout JSONWorkMeter) throws -> [UInt8] { + try meter.resetValueBounds() + return try JSONWriter(options: .init(keyOrder: .scalar, numberStyle: .normalized)).write(value, meter: &meter) + } + static func construct(_ value: JSON, meter: inout JSONWorkMeter) throws -> JSON { + let encoded = try bytes(value, meter: &meter) + try meter.resetValueBounds() + let normalized = try JSONParser().parse(encoded, meter: &meter) + try meter.release(UInt64(encoded.count)) + return normalized + } +} + +enum SchemaExampleDigests { + static func snapshot(document: JSONSchemaDocument, registry: JSONSchemaRegistry, + meter: inout JSONWorkMeter) throws -> String { + var transport = try SchemaExampleTransport(header: "SwiftJSON-schema-snapshot-v1\n", meter: &meter) + let count = try meter.adding(.schemaDocuments, UInt64(registry.documents.count), 1) + try transport.integer(count, meter: &meter) + for index in 0.. String { + var transport = try SchemaExampleTransport(header: "SwiftJSON-example-corpus-v1\n", meter: &meter) + let encoded = try SchemaExampleExact.bytes(.array(values), meter: &meter) + try transport.field(encoded, meter: &meter); try meter.release(UInt64(encoded.count)) + return try transport.digest(meter: &meter) + } + static func policy(_ options: JSONSchemaExampleOptions, meter: inout JSONWorkMeter) throws -> String { + var transport = try SchemaExampleTransport(header: "SwiftJSON-example-policy-v1\n", meter: &meter) + try transport.integer(options.maximumAttempts, meter: &meter) + try meter.retain(JSONLogicalWeights.arraySlot * 3) + for limits in [options.limits, options.compilationOptions.limits, options.validationOptions.limits] { + try transport.integer(UInt64(JSONResourceMetric.allCases.count), meter: &meter) + for metric in JSONResourceMetric.allCases { + try transport.field(metric.rawValue.utf8, meter: &meter) + try transport.integer(limits[metric], meter: &meter) + } + } + try meter.release(JSONLogicalWeights.arraySlot * 3) + try meter.retain(JSONLogicalWeights.arraySlot * 2) + for policy in [options.compilationOptions, options.validationOptions] { + try transport.append(policy.formatAssertion ? 1 : 0, meter: &meter) + try transport.append(policy.output == .flag ? 0 : 1, meter: &meter) + try transport.append(policy.diagnosticPolicy == .complete ? 0 : 1, meter: &meter) + try transport.append(policy.collectAnnotations ? 1 : 0, meter: &meter) + } + try meter.release(JSONLogicalWeights.arraySlot * 2) + return try transport.digest(meter: &meter) + } +} + +// Swift infers Sendable for internal value types with Sendable storage. Explicit +// unavailable conformances opt invocation-only mutable helpers out, with compiler +// rejection fixtures; immutable preparation remains checked Sendable. +@available(*, unavailable) +extension SchemaExampleRandom: Sendable {} +@available(*, unavailable) +extension SchemaExampleTrace: Sendable {} +@available(*, unavailable) +extension SchemaExampleTransport: Sendable {} diff --git a/Sources/JSONSchemaExamples/SchemaExamplePattern.swift b/Sources/JSONSchemaExamples/SchemaExamplePattern.swift new file mode 100644 index 0000000..2970c99 --- /dev/null +++ b/Sources/JSONSchemaExamples/SchemaExamplePattern.swift @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSON + +// Proposal scanner only, never a regex matcher or schema-validity authority. +// Fully anchored sequences of printable literals, escaped punctuation or single +// ASCII ranges. Each atom may carry one exact {n}; other quantifiers are unsupported. +enum SchemaExamplePattern { + static let punctuation: [UInt8] = [92,94,36,46,124,63,42,43,40,41,91,93,123,125,47,45] + static func propose(_ pattern: String, variation: UInt64, minimum: UInt64, maximum: UInt64, + meter: inout JSONWorkMeter) throws -> String? { + var source: [UInt8] = [] + for byte in pattern.utf8 { + try meter.work(); try meter.require(.regexPatternBytes, UInt64(source.count) + 1) + try meter.retain(1); source.append(byte) + } + guard source.count >= 2, source.first == 94, source.last == 36 else { return nil } + let end = source.count - 1 + var position = 1, output: [UInt8] = [] + while position < end { + try meter.work() + let first = source[position], byte: UInt8 + if first == 92 { + guard position + 1 < end else { return nil } + let escaped = source[position + 1] + var accepted = false + for symbol in punctuation { try meter.work(); if symbol == escaped { accepted = true; break } } + guard accepted else { return nil } + byte = escaped; position += 2 + } else if first == 91 { + guard end - position >= 5, source[position + 2] == 45, source[position + 4] == 93 else { return nil } + let lower = source[position + 1], upper = source[position + 3] + guard lower >= 32, upper <= 126, lower <= upper, + lower != 92, lower != 94, lower != 93, upper != 92, upper != 93 else { return nil } + byte = lower + UInt8(variation % UInt64(upper - lower + 1)); position += 5 + } else { + guard first >= 32, first <= 126 else { return nil } + var special = false + for symbol in punctuation { try meter.work(); if symbol == first && symbol != 47 && symbol != 45 { special = true; break } } + guard !special else { return nil } + byte = first; position += 1 + } + var repetitions: UInt64 = 1 + if position < end && source[position] == 123 { + position += 1; repetitions = 0 + let start = position + while position < end && source[position] >= 48 && source[position] <= 57 { + try meter.work(subsystem: .numericWorkUnits) + let (product, badProduct) = repetitions.multipliedReportingOverflow(by: 10) + let (next, badSum) = product.addingReportingOverflow(UInt64(source[position] - 48)) + guard !badProduct, !badSum else { + let failure = JSONFailure(.resource, code: .counterOverflow) + meter.recordFailure(failure); throw failure + } + try meter.require(.stringBytes, next) + repetitions = next; position += 1 + } + guard position > start, position < end, source[position] == 125 else { return nil } + position += 1 + } + let length = try meter.adding(.stringBytes, UInt64(output.count), repetitions) + guard length <= maximum else { return nil } + for _ in 0..= minimum else { return nil } + for _ in output { try meter.work(); try meter.retain(1) } + return String(decoding: output, as: UTF8.self) + } +} diff --git a/Sources/JSONSchemaExamples/SchemaExamplePrimitives.swift b/Sources/JSONSchemaExamples/SchemaExamplePrimitives.swift new file mode 100644 index 0000000..46d3bad --- /dev/null +++ b/Sources/JSONSchemaExamples/SchemaExamplePrimitives.swift @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSON +import JSONSchema + +// Conservative hints only. Complete native validation, not these proposals, +// establishes whether all constraints on the resulting instance are satisfied. +enum SchemaExamplePrimitives { + struct Bound { let number: JSONNumber; let open: Bool } + static func number(_ constraints: [SchemaExampleConstraint], integral: Bool, variation: UInt64, + meter: inout JSONWorkMeter) throws -> JSONNumber? { + try meter.retain(64) + var lower: Bound?, upper: Bound?, step: JSONNumber?, precision: Int64 = 0 + for c in constraints { + for kind in [JSONSchemaPlan.Keyword.minimum, .exclusiveMinimum, .maximum, .exclusiveMaximum] { + guard let n = try c.value(kind, meter: &meter)?.numberValue else { continue } + precision = min(precision, n.components.exponent) + let candidate = Bound(number: n, open: kind == .exclusiveMinimum || kind == .exclusiveMaximum) + if kind == .minimum || kind == .exclusiveMinimum { + if let prior = lower { + let order = try n.compare(to: prior.number, meter: &meter) + if order == .orderedDescending || order == .orderedSame && candidate.open { lower = candidate } + } else { lower = candidate } + } else { + if let prior = upper { + let order = try n.compare(to: prior.number, meter: &meter) + if order == .orderedAscending || order == .orderedSame && candidate.open { upper = candidate } + } else { upper = candidate } + } + } + if step == nil { step = try c.value(.multipleOf, meter: &meter)?.numberValue } + } + if let lower, let upper { + let order = try lower.number.compare(to: upper.number, meter: &meter) + if order == .orderedDescending || order == .orderedSame && (lower.open || upper.open) { return nil } + } + let stride: JSONNumber + if let step { + stride = integral ? try SchemaExampleDecimal.integralStep(step, meter: &meter) : step + } else { + let exponent = integral ? 0 : precision - 1 // admitted exponent magnitudes fit Int capacity, safely below Int64.max + try meter.require(.absoluteExponent, exponent.magnitude) + try meter.retain(1) + stride = JSONNumber(components: .init(negative: false, digits: [1], exponent: exponent)) + } + func fits(_ n: JSONNumber, meter: inout JSONWorkMeter) throws -> Bool { + if let lower { + let order = try n.compare(to: lower.number, meter: &meter) + if order == .orderedAscending || order == .orderedSame && lower.open { return false } + } + if let upper { + let order = try n.compare(to: upper.number, meter: &meter) + if order == .orderedDescending || order == .orderedSame && upper.open { return false } + } + return true + } + let zero = JSONNumber(integer: 0) + let candidate: JSONNumber + if try fits(zero, meter: &meter) { + candidate = try SchemaExampleDecimal.rounded(zero, step: stride, up: true, open: false, variation: variation, meter: &meter) + } else if let lower { + candidate = try SchemaExampleDecimal.rounded(lower.number, step: stride, up: true, open: lower.open, variation: variation, meter: &meter) + } else if let upper { + candidate = try SchemaExampleDecimal.rounded(upper.number, step: stride, up: false, open: upper.open, variation: variation, meter: &meter) + } else { return nil } + return try fits(candidate, meter: &meter) ? candidate : nil + } + + static func size(_ constraints: [SchemaExampleConstraint], minimum: JSONSchemaPlan.Keyword, + maximum: JSONSchemaPlan.Keyword, capacity: JSONResourceMetric, + meter: inout JSONWorkMeter) throws -> (minimum: UInt64, maximum: UInt64)? { + var lo: UInt64 = 0, hi = meter.limits[capacity] + for c in constraints { + if let n = try c.value(minimum, meter: &meter)?.numberValue { lo = max(lo, try n.integer(as: UInt64.self, meter: &meter)) } + if let n = try c.value(maximum, meter: &meter)?.numberValue { hi = min(hi, try n.integer(as: UInt64.self, meter: &meter)) } + } + try meter.require(capacity, lo) + return lo <= hi ? (lo,hi) : nil + } + static func string(_ constraints: [SchemaExampleConstraint], variation: UInt64, + profile: JSONSchemaExampleProfile, corpus: [JSON], assertion: Bool, + random: inout SchemaExampleRandom, trace: inout SchemaExampleTrace, + meter: inout JSONWorkMeter) throws -> String? { + guard let bounds = try size(constraints, minimum: .minLength, maximum: .maxLength, + capacity: .stringBytes, meter: &meter) else { return nil } + for c in constraints { + if let pattern = try c.value(.pattern, meter: &meter)?.stringValue, + let proposal = try SchemaExamplePattern.propose(pattern, variation: variation, minimum: bounds.minimum, + maximum: bounds.maximum, meter: &meter) { return proposal } + } + var email = false + for c in constraints { + if c.view.formatAssertion || assertion, + let format = try c.value(.format, meter: &meter)?.stringValue, + try JSONComparison.textEquals(format, "email", meter: &meter) { email = true } + } + var token = "" + if email { token = "example@example.com" } + else if profile == .demonstration && variation == 0 { + var strings: [String] = [] + for value in corpus { + try meter.work() + if let text = value.stringValue { try meter.retain(JSONLogicalWeights.indexSlot); strings.append(text) } + } + if !strings.isEmpty { + token = strings[Int(try random.uniform(upperBound: UInt64(strings.count), trace: &trace, meter: &meter))] + } + } + var bytes: [UInt8] = [], scalars: UInt64 = 0 + func append(_ byte: UInt8, meter: inout JSONWorkMeter) throws { + try meter.work(); try meter.require(.stringBytes, UInt64(bytes.count) + 1) + try meter.charge(.aggregateTextBytes); try meter.retain(1); bytes.append(byte) + } + for scalar in token.unicodeScalars { + try meter.work() + if scalars == bounds.maximum { break } + // A Unicode scalar has at most four bytes. Charge the bounded temporary + // before native scalar encoding, then the actual output bytes. + try meter.retain(4) + for byte in String(scalar).utf8 { try append(byte, meter: &meter) } + try meter.release(4); scalars += 1 + } + var length = bounds.minimum + if variation > 0 && length == 0 { length = min(1, bounds.maximum) } + while scalars < length { + try append(UInt8(97 + variation % 26), meter: &meter); scalars += 1 + } + // String's owned UTF-8 result overlaps its construction buffer. + for _ in bytes { try meter.work(); try meter.retain(1) } + return String(decoding: bytes, as: UTF8.self) + } +} diff --git a/Tests/Fixtures/SchemaExamples/Manifest.json b/Tests/Fixtures/SchemaExamples/Manifest.json new file mode 100644 index 0000000..c02d1d7 --- /dev/null +++ b/Tests/Fixtures/SchemaExamples/Manifest.json @@ -0,0 +1,62 @@ +{ + "version": 1, + "packet_sha256": "f67ec2f5d0bfd223f54a3577f93c03951a7992ecab524910ba44f6bc8621c4a1", + "original_sha256": "6f2bfd4fdb7c7e206b6570c2156ca8e248831840733b0258aa8cd5938b35649f", + "amended_sha256": "5b3e6e40e83439a810d0eb7ac412ce158a4e582f36b9d36d6e313c159654ddd3", + "amendment": "G0: 9007199254740994.0 is mathematically integral and valid; add 9007199254740994.5 as negative. Original retained unchanged.", + "g3_must_generate_ids": [ + "boolean-true", + "null-kind", + "string-length", + "exact-integer-beyond-2pow53", + "exact-decimal-multiple-of", + "numeric-exclusive-near-zero", + "object-required-and-null", + "unicode-exact-scalar-keys", + "undeclared-required-key", + "object-min-max-properties", + "array-prefix-items", + "array-unique", + "enum", + "const-exact-decimal", + "anyof", + "allof-object-composition", + "local-ref", + "format-annotation", + "format-assertion", + "invalid-default-annotation", + "invalid-examples-annotation", + "false-nested-schema", + "object-number-key-strings", + "required-with-additional-properties-false" + ], + "g3_sound_proof_ids": [ + "boolean-false", + "not-true" + ], + "other_gate_cases": 18, + "case_count": 44, + "amended_witnesses": 155, + "g4_must_generate_ids": [ + "array-contains", + "condition-then-else", + "dependent-required", + "dependent-schema", + "pattern-properties", + "property-names", + "unevaluated-properties-composition", + "simple-regex", + "mincontains-with-prefixitems", + "array-mixed-evaluation" + ], + "g4_may_fail_ids": [ + "oneof-overlap", + "recursive-tree", + "dynamic-ref", + "conflicting-allof", + "unfulfillable-unique-enum", + "external-offline-ref", + "not-string", + "oneof-overlap-limited" + ] +} diff --git a/Tests/Fixtures/SchemaExamples/S3-G7-seeded-v1.json b/Tests/Fixtures/SchemaExamples/S3-G7-seeded-v1.json new file mode 100644 index 0000000..b831095 --- /dev/null +++ b/Tests/Fixtures/SchemaExamples/S3-G7-seeded-v1.json @@ -0,0 +1,934 @@ +{ + "version": 1, + "recipe": "eight bounded grammar families x eight exact variants; deterministic seed set 0,1,42,UInt64.max", + "cases": [ + { + "id": "integer-0", + "schema": { + "type": "integer", + "minimum": 9007199254740993, + "maximum": 9007199254740996 + }, + "invalid": 9007199254740992 + }, + { + "id": "scalar-0", + "schema": { + "type": "object", + "properties": { + "é": { + "const": 0 + }, + "é": { + "const": 9007199254740993 + } + }, + "required": [ + "é", + "é" + ], + "additionalProperties": false + }, + "invalid": { + "é": 0 + } + }, + { + "id": "tuple-0", + "schema": { + "type": "array", + "prefixItems": [ + { + "const": 0 + }, + { + "type": "boolean" + } + ], + "minItems": 2, + "maxItems": 2, + "items": false + }, + "invalid": [ + 0 + ] + }, + { + "id": "conjunction-0", + "schema": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + }, + { + "maximum": 5 + } + ] + }, + "invalid": 6 + }, + { + "id": "choice-0", + "schema": { + "oneOf": [ + { + "const": 0 + }, + { + "const": "s0" + } + ] + }, + "invalid": null + }, + { + "id": "reference-0", + "schema": { + "$defs": { + "v": { + "const": 9007199254740993 + } + }, + "$ref": "#/$defs/v" + }, + "invalid": 9007199254740992 + }, + { + "id": "string-0", + "schema": { + "type": "string", + "minLength": 0, + "maxLength": 1 + }, + "invalid": "xx" + }, + { + "id": "hints-0", + "schema": { + "type": "string", + "default": 0, + "examples": [ + "v0" + ], + "enum": [ + "v0", + "z0" + ] + }, + "invalid": 0 + }, + { + "id": "integer-1", + "schema": { + "type": "integer", + "minimum": 9007199254740994, + "maximum": 9007199254740997 + }, + "invalid": 9007199254740993 + }, + { + "id": "scalar-1", + "schema": { + "type": "object", + "properties": { + "é": { + "const": 1 + }, + "é": { + "const": 9007199254740994 + } + }, + "required": [ + "é", + "é" + ], + "additionalProperties": false + }, + "invalid": { + "é": 1 + } + }, + { + "id": "tuple-1", + "schema": { + "type": "array", + "prefixItems": [ + { + "const": 1 + }, + { + "type": "boolean" + } + ], + "minItems": 2, + "maxItems": 2, + "items": false + }, + "invalid": [ + 1 + ] + }, + { + "id": "conjunction-1", + "schema": { + "type": "integer", + "allOf": [ + { + "minimum": 1 + }, + { + "maximum": 6 + } + ] + }, + "invalid": 7 + }, + { + "id": "choice-1", + "schema": { + "oneOf": [ + { + "const": 1 + }, + { + "const": "s1" + } + ] + }, + "invalid": null + }, + { + "id": "reference-1", + "schema": { + "$defs": { + "v": { + "const": 9007199254740994 + } + }, + "$ref": "#/$defs/v" + }, + "invalid": 9007199254740993 + }, + { + "id": "string-1", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 2 + }, + "invalid": "xxx" + }, + { + "id": "hints-1", + "schema": { + "type": "string", + "default": 1, + "examples": [ + "v1" + ], + "enum": [ + "v1", + "z1" + ] + }, + "invalid": 1 + }, + { + "id": "integer-2", + "schema": { + "type": "integer", + "minimum": 9007199254740995, + "maximum": 9007199254740998 + }, + "invalid": 9007199254740994 + }, + { + "id": "scalar-2", + "schema": { + "type": "object", + "properties": { + "é": { + "const": 2 + }, + "é": { + "const": 9007199254740995 + } + }, + "required": [ + "é", + "é" + ], + "additionalProperties": false + }, + "invalid": { + "é": 2 + } + }, + { + "id": "tuple-2", + "schema": { + "type": "array", + "prefixItems": [ + { + "const": 2 + }, + { + "type": "boolean" + } + ], + "minItems": 2, + "maxItems": 2, + "items": false + }, + "invalid": [ + 2 + ] + }, + { + "id": "conjunction-2", + "schema": { + "type": "integer", + "allOf": [ + { + "minimum": 2 + }, + { + "maximum": 7 + } + ] + }, + "invalid": 8 + }, + { + "id": "choice-2", + "schema": { + "oneOf": [ + { + "const": 2 + }, + { + "const": "s2" + } + ] + }, + "invalid": null + }, + { + "id": "reference-2", + "schema": { + "$defs": { + "v": { + "const": 9007199254740995 + } + }, + "$ref": "#/$defs/v" + }, + "invalid": 9007199254740994 + }, + { + "id": "string-2", + "schema": { + "type": "string", + "minLength": 2, + "maxLength": 3 + }, + "invalid": "xxxx" + }, + { + "id": "hints-2", + "schema": { + "type": "string", + "default": 2, + "examples": [ + "v2" + ], + "enum": [ + "v2", + "z2" + ] + }, + "invalid": 2 + }, + { + "id": "integer-3", + "schema": { + "type": "integer", + "minimum": 9007199254740996, + "maximum": 9007199254740999 + }, + "invalid": 9007199254740995 + }, + { + "id": "scalar-3", + "schema": { + "type": "object", + "properties": { + "é": { + "const": 3 + }, + "é": { + "const": 9007199254740996 + } + }, + "required": [ + "é", + "é" + ], + "additionalProperties": false + }, + "invalid": { + "é": 3 + } + }, + { + "id": "tuple-3", + "schema": { + "type": "array", + "prefixItems": [ + { + "const": 3 + }, + { + "type": "boolean" + } + ], + "minItems": 2, + "maxItems": 2, + "items": false + }, + "invalid": [ + 3 + ] + }, + { + "id": "conjunction-3", + "schema": { + "type": "integer", + "allOf": [ + { + "minimum": 3 + }, + { + "maximum": 8 + } + ] + }, + "invalid": 9 + }, + { + "id": "choice-3", + "schema": { + "oneOf": [ + { + "const": 3 + }, + { + "const": "s3" + } + ] + }, + "invalid": null + }, + { + "id": "reference-3", + "schema": { + "$defs": { + "v": { + "const": 9007199254740996 + } + }, + "$ref": "#/$defs/v" + }, + "invalid": 9007199254740995 + }, + { + "id": "string-3", + "schema": { + "type": "string", + "minLength": 3, + "maxLength": 4 + }, + "invalid": "xxxxx" + }, + { + "id": "hints-3", + "schema": { + "type": "string", + "default": 3, + "examples": [ + "v3" + ], + "enum": [ + "v3", + "z3" + ] + }, + "invalid": 3 + }, + { + "id": "integer-4", + "schema": { + "type": "integer", + "minimum": 9007199254740997, + "maximum": 9007199254741000 + }, + "invalid": 9007199254740996 + }, + { + "id": "scalar-4", + "schema": { + "type": "object", + "properties": { + "é": { + "const": 4 + }, + "é": { + "const": 9007199254740997 + } + }, + "required": [ + "é", + "é" + ], + "additionalProperties": false + }, + "invalid": { + "é": 4 + } + }, + { + "id": "tuple-4", + "schema": { + "type": "array", + "prefixItems": [ + { + "const": 4 + }, + { + "type": "boolean" + } + ], + "minItems": 2, + "maxItems": 2, + "items": false + }, + "invalid": [ + 4 + ] + }, + { + "id": "conjunction-4", + "schema": { + "type": "integer", + "allOf": [ + { + "minimum": 4 + }, + { + "maximum": 9 + } + ] + }, + "invalid": 10 + }, + { + "id": "choice-4", + "schema": { + "oneOf": [ + { + "const": 4 + }, + { + "const": "s4" + } + ] + }, + "invalid": null + }, + { + "id": "reference-4", + "schema": { + "$defs": { + "v": { + "const": 9007199254740997 + } + }, + "$ref": "#/$defs/v" + }, + "invalid": 9007199254740996 + }, + { + "id": "string-4", + "schema": { + "type": "string", + "minLength": 4, + "maxLength": 5 + }, + "invalid": "xxxxxx" + }, + { + "id": "hints-4", + "schema": { + "type": "string", + "default": 4, + "examples": [ + "v4" + ], + "enum": [ + "v4", + "z4" + ] + }, + "invalid": 4 + }, + { + "id": "integer-5", + "schema": { + "type": "integer", + "minimum": 9007199254740998, + "maximum": 9007199254741001 + }, + "invalid": 9007199254740997 + }, + { + "id": "scalar-5", + "schema": { + "type": "object", + "properties": { + "é": { + "const": 5 + }, + "é": { + "const": 9007199254740998 + } + }, + "required": [ + "é", + "é" + ], + "additionalProperties": false + }, + "invalid": { + "é": 5 + } + }, + { + "id": "tuple-5", + "schema": { + "type": "array", + "prefixItems": [ + { + "const": 5 + }, + { + "type": "boolean" + } + ], + "minItems": 2, + "maxItems": 2, + "items": false + }, + "invalid": [ + 5 + ] + }, + { + "id": "conjunction-5", + "schema": { + "type": "integer", + "allOf": [ + { + "minimum": 5 + }, + { + "maximum": 10 + } + ] + }, + "invalid": 11 + }, + { + "id": "choice-5", + "schema": { + "oneOf": [ + { + "const": 5 + }, + { + "const": "s5" + } + ] + }, + "invalid": null + }, + { + "id": "reference-5", + "schema": { + "$defs": { + "v": { + "const": 9007199254740998 + } + }, + "$ref": "#/$defs/v" + }, + "invalid": 9007199254740997 + }, + { + "id": "string-5", + "schema": { + "type": "string", + "minLength": 5, + "maxLength": 6 + }, + "invalid": "xxxxxxx" + }, + { + "id": "hints-5", + "schema": { + "type": "string", + "default": 5, + "examples": [ + "v5" + ], + "enum": [ + "v5", + "z5" + ] + }, + "invalid": 5 + }, + { + "id": "integer-6", + "schema": { + "type": "integer", + "minimum": 9007199254740999, + "maximum": 9007199254741002 + }, + "invalid": 9007199254740998 + }, + { + "id": "scalar-6", + "schema": { + "type": "object", + "properties": { + "é": { + "const": 6 + }, + "é": { + "const": 9007199254740999 + } + }, + "required": [ + "é", + "é" + ], + "additionalProperties": false + }, + "invalid": { + "é": 6 + } + }, + { + "id": "tuple-6", + "schema": { + "type": "array", + "prefixItems": [ + { + "const": 6 + }, + { + "type": "boolean" + } + ], + "minItems": 2, + "maxItems": 2, + "items": false + }, + "invalid": [ + 6 + ] + }, + { + "id": "conjunction-6", + "schema": { + "type": "integer", + "allOf": [ + { + "minimum": 6 + }, + { + "maximum": 11 + } + ] + }, + "invalid": 12 + }, + { + "id": "choice-6", + "schema": { + "oneOf": [ + { + "const": 6 + }, + { + "const": "s6" + } + ] + }, + "invalid": null + }, + { + "id": "reference-6", + "schema": { + "$defs": { + "v": { + "const": 9007199254740999 + } + }, + "$ref": "#/$defs/v" + }, + "invalid": 9007199254740998 + }, + { + "id": "string-6", + "schema": { + "type": "string", + "minLength": 6, + "maxLength": 7 + }, + "invalid": "xxxxxxxx" + }, + { + "id": "hints-6", + "schema": { + "type": "string", + "default": 6, + "examples": [ + "v6" + ], + "enum": [ + "v6", + "z6" + ] + }, + "invalid": 6 + }, + { + "id": "integer-7", + "schema": { + "type": "integer", + "minimum": 9007199254741000, + "maximum": 9007199254741003 + }, + "invalid": 9007199254740999 + }, + { + "id": "scalar-7", + "schema": { + "type": "object", + "properties": { + "é": { + "const": 7 + }, + "é": { + "const": 9007199254741000 + } + }, + "required": [ + "é", + "é" + ], + "additionalProperties": false + }, + "invalid": { + "é": 7 + } + }, + { + "id": "tuple-7", + "schema": { + "type": "array", + "prefixItems": [ + { + "const": 7 + }, + { + "type": "boolean" + } + ], + "minItems": 2, + "maxItems": 2, + "items": false + }, + "invalid": [ + 7 + ] + }, + { + "id": "conjunction-7", + "schema": { + "type": "integer", + "allOf": [ + { + "minimum": 7 + }, + { + "maximum": 12 + } + ] + }, + "invalid": 13 + }, + { + "id": "choice-7", + "schema": { + "oneOf": [ + { + "const": 7 + }, + { + "const": "s7" + } + ] + }, + "invalid": null + }, + { + "id": "reference-7", + "schema": { + "$defs": { + "v": { + "const": 9007199254741000 + } + }, + "$ref": "#/$defs/v" + }, + "invalid": 9007199254740999 + }, + { + "id": "string-7", + "schema": { + "type": "string", + "minLength": 7, + "maxLength": 8 + }, + "invalid": "xxxxxxxxx" + }, + { + "id": "hints-7", + "schema": { + "type": "string", + "default": 7, + "examples": [ + "v7" + ], + "enum": [ + "v7", + "z7" + ] + }, + "invalid": 7 + } + ] +} diff --git a/Tests/Fixtures/SchemaExamples/S3-integral-correction-v1.json b/Tests/Fixtures/SchemaExamples/S3-integral-correction-v1.json new file mode 100644 index 0000000..1aa8f2b --- /dev/null +++ b/Tests/Fixtures/SchemaExamples/S3-integral-correction-v1.json @@ -0,0 +1,2152 @@ +{ + "fixtureFormat": "SwiftJSON.SchemaExamples.Cases/1", + "draft": "https://json-schema.org/draft/2020-12/schema", + "intendedFor": "Independent correctness oracle and Codex acceptance gates; not production generator outputs", + "cases": [ + { + "id": "boolean-true", + "generatorExpectation": "must-generate", + "featureTags": [ + "boolean" + ], + "schema": true, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": null, + "valid": true + }, + { + "label": "valid_2", + "instance": {}, + "valid": true + }, + { + "label": "valid_3", + "instance": [], + "valid": true + }, + { + "label": "valid_4", + "instance": 42, + "valid": true + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "boolean-false", + "generatorExpectation": "must-prove-unsatisfiable", + "featureTags": [ + "boolean", + "unsatisfiable" + ], + "schema": false, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "invalid_1", + "instance": null, + "valid": false + }, + { + "label": "invalid_2", + "instance": {}, + "valid": false + }, + { + "label": "invalid_3", + "instance": 12, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "not-true", + "generatorExpectation": "must-prove-unsatisfiable", + "featureTags": [ + "not", + "unsatisfiable" + ], + "schema": { + "not": {} + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "invalid_1", + "instance": null, + "valid": false + }, + { + "label": "invalid_2", + "instance": {}, + "valid": false + }, + { + "label": "invalid_3", + "instance": "x", + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "null-kind", + "generatorExpectation": "must-generate", + "featureTags": [ + "type" + ], + "schema": { + "type": "null" + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": null, + "valid": true + }, + { + "label": "invalid_1", + "instance": false, + "valid": false + }, + { + "label": "invalid_2", + "instance": 0, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "string-length", + "generatorExpectation": "must-generate", + "featureTags": [ + "string", + "unicode", + "minLength", + "maxLength" + ], + "schema": { + "type": "string", + "minLength": 2, + "maxLength": 4 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": "ab", + "valid": true + }, + { + "label": "valid_2", + "instance": "abc", + "valid": true + }, + { + "label": "valid_3", + "instance": "é", + "valid": true + }, + { + "label": "invalid_1", + "instance": "", + "valid": false + }, + { + "label": "invalid_2", + "instance": "é", + "valid": false + }, + { + "label": "invalid_3", + "instance": "abcde", + "valid": false + }, + { + "label": "invalid_4", + "instance": 7, + "valid": false + } + ], + "notes": "Length is Unicode scalars, not grapheme clusters", + "acceptanceGate": "G3" + }, + { + "id": "exact-integer-beyond-2pow53", + "generatorExpectation": "must-generate", + "featureTags": [ + "integer", + "exactNumber" + ], + "schema": { + "type": "integer", + "minimum": 9007199254740993, + "maximum": 9007199254740995 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 9007199254740993, + "valid": true + }, + { + "label": "valid_2", + "instance": 9007199254740994, + "valid": true + }, + { + "label": "invalid_1", + "instance": 9007199254740992, + "valid": false + }, + { + "label": "invalid_2", + "instance": 9007199254740996, + "valid": false + }, + { + "label": "valid_integral_decimal_lexeme", + "instance": 9007199254740994.0, + "valid": true + }, + { + "label": "invalid_fractional_number", + "instance": 9007199254740994.5, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "exact-decimal-multiple-of", + "generatorExpectation": "must-generate", + "featureTags": [ + "exactNumber", + "multipleOf", + "exclusiveMaximum" + ], + "schema": { + "type": "number", + "multipleOf": 0.01, + "minimum": 0.02, + "exclusiveMaximum": 0.05 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 0.02, + "valid": true + }, + { + "label": "valid_2", + "instance": 0.03, + "valid": true + }, + { + "label": "valid_3", + "instance": 0.04, + "valid": true + }, + { + "label": "invalid_1", + "instance": 0.05, + "valid": false + }, + { + "label": "invalid_2", + "instance": 0.025, + "valid": false + }, + { + "label": "invalid_3", + "instance": 0.01, + "valid": false + } + ], + "notes": "Parse JSON numeric fractions as Decimal for independent Python oracle", + "acceptanceGate": "G3" + }, + { + "id": "numeric-exclusive-near-zero", + "generatorExpectation": "must-generate", + "featureTags": [ + "exactNumber", + "multipleOf", + "exclusiveMinimum", + "exclusiveMaximum" + ], + "schema": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 0.001, + "multipleOf": 0.0001 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 0.0001, + "valid": true + }, + { + "label": "valid_2", + "instance": 0.0005, + "valid": true + }, + { + "label": "invalid_1", + "instance": 0, + "valid": false + }, + { + "label": "invalid_2", + "instance": 0.001, + "valid": false + }, + { + "label": "invalid_3", + "instance": 0.00015, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "object-required-and-null", + "generatorExpectation": "must-generate", + "featureTags": [ + "object", + "required", + "null", + "additionalProperties" + ], + "schema": { + "type": "object", + "properties": { + "x": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "x" + ], + "additionalProperties": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "x": null + }, + "valid": true + }, + { + "label": "valid_2", + "instance": { + "x": "abc" + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": {}, + "valid": false + }, + { + "label": "invalid_2", + "instance": { + "x": 1 + }, + "valid": false + }, + { + "label": "invalid_3", + "instance": { + "x": "a", + "y": 0 + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "unicode-exact-scalar-keys", + "generatorExpectation": "must-generate", + "featureTags": [ + "unicode", + "object", + "required", + "exactKeys" + ], + "schema": { + "type": "object", + "properties": { + "é": { + "const": 1 + }, + "é": { + "const": 2 + } + }, + "required": [ + "é", + "é" + ], + "additionalProperties": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "é": 1, + "é": 2 + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "é": 1 + }, + "valid": false + }, + { + "label": "invalid_2", + "instance": { + "é": 2 + }, + "valid": false + }, + { + "label": "invalid_3", + "instance": { + "é": 2, + "é": 1 + }, + "valid": false + } + ], + "notes": "Two different decoded scalar sequences, even when canonically equivalent", + "acceptanceGate": "G3" + }, + { + "id": "undeclared-required-key", + "generatorExpectation": "must-generate", + "featureTags": [ + "required", + "additionalProperties" + ], + "schema": { + "type": "object", + "required": [ + "id" + ], + "additionalProperties": { + "type": "integer" + } + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "id": 0 + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": {}, + "valid": false + }, + { + "label": "invalid_2", + "instance": { + "id": null + }, + "valid": false + } + ], + "notes": "Required key need not have a properties declaration", + "acceptanceGate": "G3" + }, + { + "id": "object-min-max-properties", + "generatorExpectation": "must-generate", + "featureTags": [ + "minProperties", + "maxProperties", + "additionalProperties" + ], + "schema": { + "type": "object", + "minProperties": 2, + "maxProperties": 2, + "additionalProperties": { + "type": "integer" + } + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "a": 1, + "b": 2 + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": {}, + "valid": false + }, + { + "label": "invalid_2", + "instance": { + "a": 1 + }, + "valid": false + }, + { + "label": "invalid_3", + "instance": { + "a": 1, + "b": 2, + "c": 3 + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "array-prefix-items", + "generatorExpectation": "must-generate", + "featureTags": [ + "prefixItems", + "items", + "minItems" + ], + "schema": { + "type": "array", + "prefixItems": [ + { + "type": "string" + }, + { + "type": "integer" + } + ], + "items": false, + "minItems": 2 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": [ + "item", + 42 + ], + "valid": true + }, + { + "label": "invalid_1", + "instance": [ + "item" + ], + "valid": false + }, + { + "label": "invalid_2", + "instance": [ + "item", + 42, + true + ], + "valid": false + }, + { + "label": "invalid_3", + "instance": [ + 4, + 42 + ], + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "array-unique", + "generatorExpectation": "must-generate", + "featureTags": [ + "uniqueItems", + "minItems", + "maxItems" + ], + "schema": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": { + "type": "integer" + }, + "uniqueItems": true + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": [ + 1, + 2, + 3 + ], + "valid": true + }, + { + "label": "invalid_1", + "instance": [ + 1, + 1, + 2 + ], + "valid": false + }, + { + "label": "invalid_2", + "instance": [ + 1, + 2 + ], + "valid": false + }, + { + "label": "invalid_3", + "instance": [ + "a", + "b", + "c" + ], + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "array-contains", + "generatorExpectation": "must-generate", + "featureTags": [ + "contains", + "minContains", + "maxContains" + ], + "schema": { + "type": "array", + "items": { + "type": "integer" + }, + "contains": { + "multipleOf": 2 + }, + "minContains": 2, + "maxContains": 2 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": [ + 1, + 2, + 4 + ], + "valid": true + }, + { + "label": "valid_2", + "instance": [ + 2, + 4, + 5 + ], + "valid": true + }, + { + "label": "invalid_1", + "instance": [ + 1, + 3, + 5 + ], + "valid": false + }, + { + "label": "invalid_2", + "instance": [ + 2, + 4, + 6 + ], + "valid": false + }, + { + "label": "invalid_3", + "instance": [ + 1, + 2, + "hi" + ], + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "enum", + "generatorExpectation": "must-generate", + "featureTags": [ + "enum" + ], + "schema": { + "enum": [ + "red", + "green", + "blue" + ] + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": "red", + "valid": true + }, + { + "label": "valid_2", + "instance": "green", + "valid": true + }, + { + "label": "invalid_1", + "instance": "purple", + "valid": false + }, + { + "label": "invalid_2", + "instance": null, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "const-exact-decimal", + "generatorExpectation": "must-generate", + "featureTags": [ + "const", + "exactNumber" + ], + "schema": { + "const": 0.1 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 0.1, + "valid": true + }, + { + "label": "invalid_1", + "instance": 0.2, + "valid": false + }, + { + "label": "invalid_2", + "instance": 1, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "anyof", + "generatorExpectation": "must-generate", + "featureTags": [ + "anyOf" + ], + "schema": { + "anyOf": [ + { + "type": "string", + "minLength": 2 + }, + { + "type": "integer", + "minimum": 8 + } + ] + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": "ok", + "valid": true + }, + { + "label": "valid_2", + "instance": 9, + "valid": true + }, + { + "label": "invalid_1", + "instance": "a", + "valid": false + }, + { + "label": "invalid_2", + "instance": 7, + "valid": false + }, + { + "label": "invalid_3", + "instance": true, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "oneof-overlap", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "oneOf", + "overlap" + ], + "schema": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "integer" + } + ] + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 0.5, + "valid": true + }, + { + "label": "invalid_1", + "instance": 0, + "valid": false + }, + { + "label": "invalid_2", + "instance": 1, + "valid": false + }, + { + "label": "invalid_3", + "instance": "abc", + "valid": false + } + ], + "notes": "A number that is mathematically integer satisfies both branches and is invalid under oneOf", + "acceptanceGate": "G4" + }, + { + "id": "allof-object-composition", + "generatorExpectation": "must-generate", + "featureTags": [ + "allOf", + "object" + ], + "schema": { + "type": "object", + "allOf": [ + { + "properties": { + "a": { + "type": "integer" + } + }, + "required": [ + "a" + ] + }, + { + "properties": { + "b": { + "type": "string" + } + }, + "required": [ + "b" + ] + } + ], + "additionalProperties": true + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "a": 1, + "b": "x" + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "a": 1 + }, + "valid": false + }, + { + "label": "invalid_2", + "instance": { + "a": "wrong", + "b": "x" + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "condition-then-else", + "generatorExpectation": "must-generate", + "featureTags": [ + "if", + "then", + "else", + "const", + "required" + ], + "schema": { + "type": "object", + "properties": { + "kind": { + "enum": [ + "adult", + "child" + ] + }, + "age": { + "type": "integer" + } + }, + "required": [ + "kind", + "age" + ], + "if": { + "properties": { + "kind": { + "const": "adult" + } + }, + "required": [ + "kind" + ] + }, + "then": { + "properties": { + "age": { + "minimum": 18 + } + } + }, + "else": { + "properties": { + "age": { + "maximum": 17 + } + } + } + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "kind": "adult", + "age": 18 + }, + "valid": true + }, + { + "label": "valid_2", + "instance": { + "kind": "child", + "age": 12 + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "kind": "adult", + "age": 17 + }, + "valid": false + }, + { + "label": "invalid_2", + "instance": { + "kind": "child", + "age": 18 + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "dependent-required", + "generatorExpectation": "must-generate", + "featureTags": [ + "dependentRequired" + ], + "schema": { + "type": "object", + "properties": { + "creditCard": { + "type": "string" + }, + "billingAddress": { + "type": "string" + } + }, + "dependentRequired": { + "creditCard": [ + "billingAddress" + ] + } + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": {}, + "valid": true + }, + { + "label": "valid_2", + "instance": { + "creditCard": "1234", + "billingAddress": "x" + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "creditCard": "1234" + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "dependent-schema", + "generatorExpectation": "must-generate", + "featureTags": [ + "dependentSchemas" + ], + "schema": { + "type": "object", + "properties": { + "country": { + "type": "string" + }, + "state": { + "type": "string" + } + }, + "dependentSchemas": { + "country": { + "required": [ + "state" + ] + } + } + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": {}, + "valid": true + }, + { + "label": "valid_2", + "instance": { + "country": "US", + "state": "CA" + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "country": "US" + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "pattern-properties", + "generatorExpectation": "must-generate", + "featureTags": [ + "patternProperties", + "additionalProperties" + ], + "schema": { + "type": "object", + "patternProperties": { + "^s_": { + "type": "string" + } + }, + "additionalProperties": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "s_name": "Ada" + }, + "valid": true + }, + { + "label": "valid_2", + "instance": {}, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "s_name": 42 + }, + "valid": false + }, + { + "label": "invalid_2", + "instance": { + "x": 2 + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "property-names", + "generatorExpectation": "must-generate", + "featureTags": [ + "propertyNames", + "pattern" + ], + "schema": { + "type": "object", + "propertyNames": { + "pattern": "^[A-Z]+$" + }, + "additionalProperties": true + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "ABC": 1 + }, + "valid": true + }, + { + "label": "valid_2", + "instance": {}, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "Ab": 1 + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "unevaluated-properties-composition", + "generatorExpectation": "must-generate", + "featureTags": [ + "unevaluatedProperties", + "allOf", + "annotations" + ], + "schema": { + "type": "object", + "allOf": [ + { + "properties": { + "a": { + "type": "integer" + } + }, + "required": [ + "a" + ] + }, + { + "properties": { + "b": { + "type": "string" + } + }, + "required": [ + "b" + ] + } + ], + "unevaluatedProperties": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "a": 1, + "b": "x" + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "a": 1, + "b": "x", + "c": true + }, + "valid": false + } + ], + "notes": "Successful branches must contribute evaluated-property annotations", + "acceptanceGate": "G4" + }, + { + "id": "local-ref", + "generatorExpectation": "must-generate", + "featureTags": [ + "$ref", + "$defs" + ], + "schema": { + "$defs": { + "age": { + "type": "integer", + "minimum": 0 + } + }, + "$ref": "#/$defs/age" + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 18, + "valid": true + }, + { + "label": "invalid_1", + "instance": -1, + "valid": false + }, + { + "label": "invalid_2", + "instance": "18", + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "recursive-tree", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "$ref", + "recursion" + ], + "schema": { + "$defs": { + "node": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "children": { + "type": "array", + "items": { + "$ref": "#/$defs/node" + } + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "$ref": "#/$defs/node" + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "name": "Ada", + "children": [] + }, + "valid": true + }, + { + "label": "valid_2", + "instance": { + "name": "root", + "children": [ + { + "name": "leaf" + } + ] + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "children": [] + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "dynamic-ref", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "$dynamicRef", + "$dynamicAnchor", + "recursion" + ], + "schema": { + "$dynamicAnchor": "node", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "children": { + "type": "array", + "items": { + "$dynamicRef": "#node" + } + } + }, + "required": [ + "name" + ], + "additionalProperties": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "name": "root", + "children": [] + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "children": [] + }, + "valid": false + } + ], + "notes": "Dynamic reference has evaluation-scope-dependent meaning", + "acceptanceGate": "G4" + }, + { + "id": "simple-regex", + "generatorExpectation": "must-generate", + "featureTags": [ + "pattern", + "regex" + ], + "schema": { + "type": "string", + "pattern": "^SKU-[0-9]{4}$" + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": "SKU-1234", + "valid": true + }, + { + "label": "invalid_1", + "instance": "sku-1234", + "valid": false + }, + { + "label": "invalid_2", + "instance": "SKU-123", + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "format-annotation", + "generatorExpectation": "must-generate", + "featureTags": [ + "format", + "annotation" + ], + "schema": { + "type": "string", + "format": "email" + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": "not-an-email", + "valid": true + }, + { + "label": "valid_2", + "instance": "ada@example.org", + "valid": true + }, + { + "label": "invalid_1", + "instance": 15, + "valid": false + } + ], + "notes": "Format keyword is annotation-only under default 2020-12 dialect", + "acceptanceGate": "G3" + }, + { + "id": "format-assertion", + "generatorExpectation": "must-generate", + "featureTags": [ + "format", + "assertion" + ], + "schema": { + "type": "string", + "format": "email" + }, + "validation": { + "formatAssertion": true + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": "ada@example.org", + "valid": true + }, + { + "label": "invalid_1", + "instance": "not-an-email", + "valid": false + }, + { + "label": "invalid_2", + "instance": 15, + "valid": false + } + ], + "notes": "Optional format assertion flag enabled; independent oracle uses Python FormatChecker", + "acceptanceGate": "G3" + }, + { + "id": "conflicting-allof", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "allOf", + "unsatisfiable" + ], + "schema": { + "allOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "invalid_1", + "instance": "hello", + "valid": false + }, + { + "label": "invalid_2", + "instance": 3, + "valid": false + }, + { + "label": "invalid_3", + "instance": null, + "valid": false + } + ], + "notes": "Conservatively classify search failure unless sound proof implemented", + "acceptanceGate": "G4" + }, + { + "id": "unfulfillable-unique-enum", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "uniqueItems", + "enum", + "unsatisfiable" + ], + "schema": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "uniqueItems": true, + "items": { + "enum": [ + "a", + "b" + ] + } + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "invalid_1", + "instance": [ + "a", + "a", + "b" + ], + "valid": false + }, + { + "label": "invalid_2", + "instance": [ + "a", + "b" + ], + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "invalid-default-annotation", + "generatorExpectation": "must-generate", + "featureTags": [ + "default", + "annotation" + ], + "schema": { + "type": "integer", + "minimum": 10, + "default": 3 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 12, + "valid": true + }, + { + "label": "invalid_1", + "instance": 3, + "valid": false + } + ], + "notes": "Default is not proof of validity", + "acceptanceGate": "G3" + }, + { + "id": "invalid-examples-annotation", + "generatorExpectation": "must-generate", + "featureTags": [ + "examples", + "annotation" + ], + "schema": { + "type": "string", + "minLength": 2, + "examples": [ + "a", + "ok" + ] + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": "ok", + "valid": true + }, + { + "label": "invalid_1", + "instance": "a", + "valid": false + } + ], + "notes": "examples are untrusted candidate hints", + "acceptanceGate": "G3" + }, + { + "id": "false-nested-schema", + "generatorExpectation": "must-generate", + "featureTags": [ + "boolean", + "properties" + ], + "schema": { + "type": "object", + "properties": { + "forbidden": false + }, + "additionalProperties": true + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": {}, + "valid": true + }, + { + "label": "valid_2", + "instance": { + "other": 1 + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "forbidden": null + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "external-offline-ref", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "$ref", + "offlineRegistry", + "uri" + ], + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$ref": "urn:example:definitions:2026#/$defs/sku" + }, + "validation": { + "formatAssertion": false + }, + "resources": { + "urn:example:definitions:2026": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:example:definitions:2026", + "$defs": { + "sku": { + "type": "string", + "pattern": "^SKU-[0-9]{3}$" + } + } + } + }, + "witnesses": [ + { + "label": "valid_1", + "instance": "SKU-100", + "valid": true + }, + { + "label": "invalid_1", + "instance": "ABC", + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "not-string", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "not" + ], + "schema": { + "not": { + "type": "string" + } + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 0, + "valid": true + }, + { + "label": "valid_2", + "instance": null, + "valid": true + }, + { + "label": "valid_3", + "instance": {}, + "valid": true + }, + { + "label": "invalid_1", + "instance": "hello", + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "oneof-overlap-limited", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "oneOf", + "overlap" + ], + "schema": { + "oneOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "integer", + "maximum": 10 + } + ] + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": -1, + "valid": true + }, + { + "label": "valid_2", + "instance": 11, + "valid": true + }, + { + "label": "invalid_1", + "instance": 0, + "valid": false + }, + { + "label": "invalid_2", + "instance": 5, + "valid": false + }, + { + "label": "invalid_3", + "instance": 10, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "object-number-key-strings", + "generatorExpectation": "must-generate", + "featureTags": [ + "object", + "exactKeys" + ], + "schema": { + "type": "object", + "properties": { + "0": { + "type": "integer" + }, + "00": { + "type": "string" + } + }, + "required": [ + "0", + "00" + ], + "additionalProperties": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "0": 1, + "00": "ok" + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "0": 1 + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "mincontains-with-prefixitems", + "generatorExpectation": "must-generate", + "featureTags": [ + "prefixItems", + "contains", + "minContains", + "maxContains" + ], + "schema": { + "type": "array", + "prefixItems": [ + { + "type": "number" + } + ], + "contains": { + "type": "integer" + }, + "minContains": 1, + "maxContains": 1, + "minItems": 1 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": [ + 1.5, + 1 + ], + "valid": true + }, + { + "label": "valid_2", + "instance": [ + 1 + ], + "valid": true + }, + { + "label": "invalid_1", + "instance": [], + "valid": false + }, + { + "label": "invalid_2", + "instance": [ + 1, + 2 + ], + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "required-with-additional-properties-false", + "generatorExpectation": "must-generate", + "featureTags": [ + "additionalProperties" + ], + "schema": { + "type": "object", + "properties": { + "x": { + "type": "integer" + } + }, + "required": [ + "x" + ], + "additionalProperties": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "x": 0 + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "x": 0, + "extra": true + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "array-mixed-evaluation", + "generatorExpectation": "must-generate", + "featureTags": [ + "unevaluatedItems", + "prefixItems" + ], + "schema": { + "type": "array", + "prefixItems": [ + { + "type": "integer" + } + ], + "unevaluatedItems": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": [ + 1 + ], + "valid": true + }, + { + "label": "valid_2", + "instance": [], + "valid": true + }, + { + "label": "invalid_1", + "instance": [ + 1, + 2 + ], + "valid": false + } + ], + "notes": "[] also satisfies this schema; oracle negative list should not include []", + "acceptanceGate": "G4" + } + ] +} diff --git a/Tests/Fixtures/SchemaExamples/S3-original-v1.json b/Tests/Fixtures/SchemaExamples/S3-original-v1.json new file mode 100644 index 0000000..d0cedfe --- /dev/null +++ b/Tests/Fixtures/SchemaExamples/S3-original-v1.json @@ -0,0 +1,2147 @@ +{ + "fixtureFormat": "SwiftJSON.SchemaExamples.Cases/1", + "draft": "https://json-schema.org/draft/2020-12/schema", + "intendedFor": "Independent correctness oracle and Codex acceptance gates; not production generator outputs", + "cases": [ + { + "id": "boolean-true", + "generatorExpectation": "must-generate", + "featureTags": [ + "boolean" + ], + "schema": true, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": null, + "valid": true + }, + { + "label": "valid_2", + "instance": {}, + "valid": true + }, + { + "label": "valid_3", + "instance": [], + "valid": true + }, + { + "label": "valid_4", + "instance": 42, + "valid": true + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "boolean-false", + "generatorExpectation": "must-prove-unsatisfiable", + "featureTags": [ + "boolean", + "unsatisfiable" + ], + "schema": false, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "invalid_1", + "instance": null, + "valid": false + }, + { + "label": "invalid_2", + "instance": {}, + "valid": false + }, + { + "label": "invalid_3", + "instance": 12, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "not-true", + "generatorExpectation": "must-prove-unsatisfiable", + "featureTags": [ + "not", + "unsatisfiable" + ], + "schema": { + "not": {} + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "invalid_1", + "instance": null, + "valid": false + }, + { + "label": "invalid_2", + "instance": {}, + "valid": false + }, + { + "label": "invalid_3", + "instance": "x", + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "null-kind", + "generatorExpectation": "must-generate", + "featureTags": [ + "type" + ], + "schema": { + "type": "null" + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": null, + "valid": true + }, + { + "label": "invalid_1", + "instance": false, + "valid": false + }, + { + "label": "invalid_2", + "instance": 0, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "string-length", + "generatorExpectation": "must-generate", + "featureTags": [ + "string", + "unicode", + "minLength", + "maxLength" + ], + "schema": { + "type": "string", + "minLength": 2, + "maxLength": 4 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": "ab", + "valid": true + }, + { + "label": "valid_2", + "instance": "abc", + "valid": true + }, + { + "label": "valid_3", + "instance": "é", + "valid": true + }, + { + "label": "invalid_1", + "instance": "", + "valid": false + }, + { + "label": "invalid_2", + "instance": "é", + "valid": false + }, + { + "label": "invalid_3", + "instance": "abcde", + "valid": false + }, + { + "label": "invalid_4", + "instance": 7, + "valid": false + } + ], + "notes": "Length is Unicode scalars, not grapheme clusters", + "acceptanceGate": "G3" + }, + { + "id": "exact-integer-beyond-2pow53", + "generatorExpectation": "must-generate", + "featureTags": [ + "integer", + "exactNumber" + ], + "schema": { + "type": "integer", + "minimum": 9007199254740993, + "maximum": 9007199254740995 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 9007199254740993, + "valid": true + }, + { + "label": "valid_2", + "instance": 9007199254740994, + "valid": true + }, + { + "label": "invalid_1", + "instance": 9007199254740992, + "valid": false + }, + { + "label": "invalid_2", + "instance": 9007199254740996, + "valid": false + }, + { + "label": "invalid_3", + "instance": 9007199254740994.0, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "exact-decimal-multiple-of", + "generatorExpectation": "must-generate", + "featureTags": [ + "exactNumber", + "multipleOf", + "exclusiveMaximum" + ], + "schema": { + "type": "number", + "multipleOf": 0.01, + "minimum": 0.02, + "exclusiveMaximum": 0.05 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 0.02, + "valid": true + }, + { + "label": "valid_2", + "instance": 0.03, + "valid": true + }, + { + "label": "valid_3", + "instance": 0.04, + "valid": true + }, + { + "label": "invalid_1", + "instance": 0.05, + "valid": false + }, + { + "label": "invalid_2", + "instance": 0.025, + "valid": false + }, + { + "label": "invalid_3", + "instance": 0.01, + "valid": false + } + ], + "notes": "Parse JSON numeric fractions as Decimal for independent Python oracle", + "acceptanceGate": "G3" + }, + { + "id": "numeric-exclusive-near-zero", + "generatorExpectation": "must-generate", + "featureTags": [ + "exactNumber", + "multipleOf", + "exclusiveMinimum", + "exclusiveMaximum" + ], + "schema": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 0.001, + "multipleOf": 0.0001 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 0.0001, + "valid": true + }, + { + "label": "valid_2", + "instance": 0.0005, + "valid": true + }, + { + "label": "invalid_1", + "instance": 0, + "valid": false + }, + { + "label": "invalid_2", + "instance": 0.001, + "valid": false + }, + { + "label": "invalid_3", + "instance": 0.00015, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "object-required-and-null", + "generatorExpectation": "must-generate", + "featureTags": [ + "object", + "required", + "null", + "additionalProperties" + ], + "schema": { + "type": "object", + "properties": { + "x": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "x" + ], + "additionalProperties": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "x": null + }, + "valid": true + }, + { + "label": "valid_2", + "instance": { + "x": "abc" + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": {}, + "valid": false + }, + { + "label": "invalid_2", + "instance": { + "x": 1 + }, + "valid": false + }, + { + "label": "invalid_3", + "instance": { + "x": "a", + "y": 0 + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "unicode-exact-scalar-keys", + "generatorExpectation": "must-generate", + "featureTags": [ + "unicode", + "object", + "required", + "exactKeys" + ], + "schema": { + "type": "object", + "properties": { + "é": { + "const": 1 + }, + "é": { + "const": 2 + } + }, + "required": [ + "é", + "é" + ], + "additionalProperties": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "é": 1, + "é": 2 + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "é": 1 + }, + "valid": false + }, + { + "label": "invalid_2", + "instance": { + "é": 2 + }, + "valid": false + }, + { + "label": "invalid_3", + "instance": { + "é": 2, + "é": 1 + }, + "valid": false + } + ], + "notes": "Two different decoded scalar sequences, even when canonically equivalent", + "acceptanceGate": "G3" + }, + { + "id": "undeclared-required-key", + "generatorExpectation": "must-generate", + "featureTags": [ + "required", + "additionalProperties" + ], + "schema": { + "type": "object", + "required": [ + "id" + ], + "additionalProperties": { + "type": "integer" + } + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "id": 0 + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": {}, + "valid": false + }, + { + "label": "invalid_2", + "instance": { + "id": null + }, + "valid": false + } + ], + "notes": "Required key need not have a properties declaration", + "acceptanceGate": "G3" + }, + { + "id": "object-min-max-properties", + "generatorExpectation": "must-generate", + "featureTags": [ + "minProperties", + "maxProperties", + "additionalProperties" + ], + "schema": { + "type": "object", + "minProperties": 2, + "maxProperties": 2, + "additionalProperties": { + "type": "integer" + } + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "a": 1, + "b": 2 + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": {}, + "valid": false + }, + { + "label": "invalid_2", + "instance": { + "a": 1 + }, + "valid": false + }, + { + "label": "invalid_3", + "instance": { + "a": 1, + "b": 2, + "c": 3 + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "array-prefix-items", + "generatorExpectation": "must-generate", + "featureTags": [ + "prefixItems", + "items", + "minItems" + ], + "schema": { + "type": "array", + "prefixItems": [ + { + "type": "string" + }, + { + "type": "integer" + } + ], + "items": false, + "minItems": 2 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": [ + "item", + 42 + ], + "valid": true + }, + { + "label": "invalid_1", + "instance": [ + "item" + ], + "valid": false + }, + { + "label": "invalid_2", + "instance": [ + "item", + 42, + true + ], + "valid": false + }, + { + "label": "invalid_3", + "instance": [ + 4, + 42 + ], + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "array-unique", + "generatorExpectation": "must-generate", + "featureTags": [ + "uniqueItems", + "minItems", + "maxItems" + ], + "schema": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": { + "type": "integer" + }, + "uniqueItems": true + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": [ + 1, + 2, + 3 + ], + "valid": true + }, + { + "label": "invalid_1", + "instance": [ + 1, + 1, + 2 + ], + "valid": false + }, + { + "label": "invalid_2", + "instance": [ + 1, + 2 + ], + "valid": false + }, + { + "label": "invalid_3", + "instance": [ + "a", + "b", + "c" + ], + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "array-contains", + "generatorExpectation": "must-generate", + "featureTags": [ + "contains", + "minContains", + "maxContains" + ], + "schema": { + "type": "array", + "items": { + "type": "integer" + }, + "contains": { + "multipleOf": 2 + }, + "minContains": 2, + "maxContains": 2 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": [ + 1, + 2, + 4 + ], + "valid": true + }, + { + "label": "valid_2", + "instance": [ + 2, + 4, + 5 + ], + "valid": true + }, + { + "label": "invalid_1", + "instance": [ + 1, + 3, + 5 + ], + "valid": false + }, + { + "label": "invalid_2", + "instance": [ + 2, + 4, + 6 + ], + "valid": false + }, + { + "label": "invalid_3", + "instance": [ + 1, + 2, + "hi" + ], + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "enum", + "generatorExpectation": "must-generate", + "featureTags": [ + "enum" + ], + "schema": { + "enum": [ + "red", + "green", + "blue" + ] + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": "red", + "valid": true + }, + { + "label": "valid_2", + "instance": "green", + "valid": true + }, + { + "label": "invalid_1", + "instance": "purple", + "valid": false + }, + { + "label": "invalid_2", + "instance": null, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "const-exact-decimal", + "generatorExpectation": "must-generate", + "featureTags": [ + "const", + "exactNumber" + ], + "schema": { + "const": 0.1 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 0.1, + "valid": true + }, + { + "label": "invalid_1", + "instance": 0.2, + "valid": false + }, + { + "label": "invalid_2", + "instance": 1, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "anyof", + "generatorExpectation": "must-generate", + "featureTags": [ + "anyOf" + ], + "schema": { + "anyOf": [ + { + "type": "string", + "minLength": 2 + }, + { + "type": "integer", + "minimum": 8 + } + ] + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": "ok", + "valid": true + }, + { + "label": "valid_2", + "instance": 9, + "valid": true + }, + { + "label": "invalid_1", + "instance": "a", + "valid": false + }, + { + "label": "invalid_2", + "instance": 7, + "valid": false + }, + { + "label": "invalid_3", + "instance": true, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "oneof-overlap", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "oneOf", + "overlap" + ], + "schema": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "integer" + } + ] + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 0.5, + "valid": true + }, + { + "label": "invalid_1", + "instance": 0, + "valid": false + }, + { + "label": "invalid_2", + "instance": 1, + "valid": false + }, + { + "label": "invalid_3", + "instance": "abc", + "valid": false + } + ], + "notes": "A number that is mathematically integer satisfies both branches and is invalid under oneOf", + "acceptanceGate": "G4" + }, + { + "id": "allof-object-composition", + "generatorExpectation": "must-generate", + "featureTags": [ + "allOf", + "object" + ], + "schema": { + "type": "object", + "allOf": [ + { + "properties": { + "a": { + "type": "integer" + } + }, + "required": [ + "a" + ] + }, + { + "properties": { + "b": { + "type": "string" + } + }, + "required": [ + "b" + ] + } + ], + "additionalProperties": true + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "a": 1, + "b": "x" + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "a": 1 + }, + "valid": false + }, + { + "label": "invalid_2", + "instance": { + "a": "wrong", + "b": "x" + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "condition-then-else", + "generatorExpectation": "must-generate", + "featureTags": [ + "if", + "then", + "else", + "const", + "required" + ], + "schema": { + "type": "object", + "properties": { + "kind": { + "enum": [ + "adult", + "child" + ] + }, + "age": { + "type": "integer" + } + }, + "required": [ + "kind", + "age" + ], + "if": { + "properties": { + "kind": { + "const": "adult" + } + }, + "required": [ + "kind" + ] + }, + "then": { + "properties": { + "age": { + "minimum": 18 + } + } + }, + "else": { + "properties": { + "age": { + "maximum": 17 + } + } + } + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "kind": "adult", + "age": 18 + }, + "valid": true + }, + { + "label": "valid_2", + "instance": { + "kind": "child", + "age": 12 + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "kind": "adult", + "age": 17 + }, + "valid": false + }, + { + "label": "invalid_2", + "instance": { + "kind": "child", + "age": 18 + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "dependent-required", + "generatorExpectation": "must-generate", + "featureTags": [ + "dependentRequired" + ], + "schema": { + "type": "object", + "properties": { + "creditCard": { + "type": "string" + }, + "billingAddress": { + "type": "string" + } + }, + "dependentRequired": { + "creditCard": [ + "billingAddress" + ] + } + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": {}, + "valid": true + }, + { + "label": "valid_2", + "instance": { + "creditCard": "1234", + "billingAddress": "x" + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "creditCard": "1234" + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "dependent-schema", + "generatorExpectation": "must-generate", + "featureTags": [ + "dependentSchemas" + ], + "schema": { + "type": "object", + "properties": { + "country": { + "type": "string" + }, + "state": { + "type": "string" + } + }, + "dependentSchemas": { + "country": { + "required": [ + "state" + ] + } + } + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": {}, + "valid": true + }, + { + "label": "valid_2", + "instance": { + "country": "US", + "state": "CA" + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "country": "US" + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "pattern-properties", + "generatorExpectation": "must-generate", + "featureTags": [ + "patternProperties", + "additionalProperties" + ], + "schema": { + "type": "object", + "patternProperties": { + "^s_": { + "type": "string" + } + }, + "additionalProperties": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "s_name": "Ada" + }, + "valid": true + }, + { + "label": "valid_2", + "instance": {}, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "s_name": 42 + }, + "valid": false + }, + { + "label": "invalid_2", + "instance": { + "x": 2 + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "property-names", + "generatorExpectation": "must-generate", + "featureTags": [ + "propertyNames", + "pattern" + ], + "schema": { + "type": "object", + "propertyNames": { + "pattern": "^[A-Z]+$" + }, + "additionalProperties": true + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "ABC": 1 + }, + "valid": true + }, + { + "label": "valid_2", + "instance": {}, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "Ab": 1 + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "unevaluated-properties-composition", + "generatorExpectation": "must-generate", + "featureTags": [ + "unevaluatedProperties", + "allOf", + "annotations" + ], + "schema": { + "type": "object", + "allOf": [ + { + "properties": { + "a": { + "type": "integer" + } + }, + "required": [ + "a" + ] + }, + { + "properties": { + "b": { + "type": "string" + } + }, + "required": [ + "b" + ] + } + ], + "unevaluatedProperties": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "a": 1, + "b": "x" + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "a": 1, + "b": "x", + "c": true + }, + "valid": false + } + ], + "notes": "Successful branches must contribute evaluated-property annotations", + "acceptanceGate": "G4" + }, + { + "id": "local-ref", + "generatorExpectation": "must-generate", + "featureTags": [ + "$ref", + "$defs" + ], + "schema": { + "$defs": { + "age": { + "type": "integer", + "minimum": 0 + } + }, + "$ref": "#/$defs/age" + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 18, + "valid": true + }, + { + "label": "invalid_1", + "instance": -1, + "valid": false + }, + { + "label": "invalid_2", + "instance": "18", + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "recursive-tree", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "$ref", + "recursion" + ], + "schema": { + "$defs": { + "node": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "children": { + "type": "array", + "items": { + "$ref": "#/$defs/node" + } + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "$ref": "#/$defs/node" + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "name": "Ada", + "children": [] + }, + "valid": true + }, + { + "label": "valid_2", + "instance": { + "name": "root", + "children": [ + { + "name": "leaf" + } + ] + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "children": [] + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "dynamic-ref", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "$dynamicRef", + "$dynamicAnchor", + "recursion" + ], + "schema": { + "$dynamicAnchor": "node", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "children": { + "type": "array", + "items": { + "$dynamicRef": "#node" + } + } + }, + "required": [ + "name" + ], + "additionalProperties": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "name": "root", + "children": [] + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "children": [] + }, + "valid": false + } + ], + "notes": "Dynamic reference has evaluation-scope-dependent meaning", + "acceptanceGate": "G4" + }, + { + "id": "simple-regex", + "generatorExpectation": "must-generate", + "featureTags": [ + "pattern", + "regex" + ], + "schema": { + "type": "string", + "pattern": "^SKU-[0-9]{4}$" + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": "SKU-1234", + "valid": true + }, + { + "label": "invalid_1", + "instance": "sku-1234", + "valid": false + }, + { + "label": "invalid_2", + "instance": "SKU-123", + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "format-annotation", + "generatorExpectation": "must-generate", + "featureTags": [ + "format", + "annotation" + ], + "schema": { + "type": "string", + "format": "email" + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": "not-an-email", + "valid": true + }, + { + "label": "valid_2", + "instance": "ada@example.org", + "valid": true + }, + { + "label": "invalid_1", + "instance": 15, + "valid": false + } + ], + "notes": "Format keyword is annotation-only under default 2020-12 dialect", + "acceptanceGate": "G3" + }, + { + "id": "format-assertion", + "generatorExpectation": "must-generate", + "featureTags": [ + "format", + "assertion" + ], + "schema": { + "type": "string", + "format": "email" + }, + "validation": { + "formatAssertion": true + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": "ada@example.org", + "valid": true + }, + { + "label": "invalid_1", + "instance": "not-an-email", + "valid": false + }, + { + "label": "invalid_2", + "instance": 15, + "valid": false + } + ], + "notes": "Optional format assertion flag enabled; independent oracle uses Python FormatChecker", + "acceptanceGate": "G3" + }, + { + "id": "conflicting-allof", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "allOf", + "unsatisfiable" + ], + "schema": { + "allOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "invalid_1", + "instance": "hello", + "valid": false + }, + { + "label": "invalid_2", + "instance": 3, + "valid": false + }, + { + "label": "invalid_3", + "instance": null, + "valid": false + } + ], + "notes": "Conservatively classify search failure unless sound proof implemented", + "acceptanceGate": "G4" + }, + { + "id": "unfulfillable-unique-enum", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "uniqueItems", + "enum", + "unsatisfiable" + ], + "schema": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "uniqueItems": true, + "items": { + "enum": [ + "a", + "b" + ] + } + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "invalid_1", + "instance": [ + "a", + "a", + "b" + ], + "valid": false + }, + { + "label": "invalid_2", + "instance": [ + "a", + "b" + ], + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "invalid-default-annotation", + "generatorExpectation": "must-generate", + "featureTags": [ + "default", + "annotation" + ], + "schema": { + "type": "integer", + "minimum": 10, + "default": 3 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 12, + "valid": true + }, + { + "label": "invalid_1", + "instance": 3, + "valid": false + } + ], + "notes": "Default is not proof of validity", + "acceptanceGate": "G3" + }, + { + "id": "invalid-examples-annotation", + "generatorExpectation": "must-generate", + "featureTags": [ + "examples", + "annotation" + ], + "schema": { + "type": "string", + "minLength": 2, + "examples": [ + "a", + "ok" + ] + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": "ok", + "valid": true + }, + { + "label": "invalid_1", + "instance": "a", + "valid": false + } + ], + "notes": "examples are untrusted candidate hints", + "acceptanceGate": "G3" + }, + { + "id": "false-nested-schema", + "generatorExpectation": "must-generate", + "featureTags": [ + "boolean", + "properties" + ], + "schema": { + "type": "object", + "properties": { + "forbidden": false + }, + "additionalProperties": true + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": {}, + "valid": true + }, + { + "label": "valid_2", + "instance": { + "other": 1 + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "forbidden": null + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "external-offline-ref", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "$ref", + "offlineRegistry", + "uri" + ], + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$ref": "urn:example:definitions:2026#/$defs/sku" + }, + "validation": { + "formatAssertion": false + }, + "resources": { + "urn:example:definitions:2026": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:example:definitions:2026", + "$defs": { + "sku": { + "type": "string", + "pattern": "^SKU-[0-9]{3}$" + } + } + } + }, + "witnesses": [ + { + "label": "valid_1", + "instance": "SKU-100", + "valid": true + }, + { + "label": "invalid_1", + "instance": "ABC", + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "not-string", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "not" + ], + "schema": { + "not": { + "type": "string" + } + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": 0, + "valid": true + }, + { + "label": "valid_2", + "instance": null, + "valid": true + }, + { + "label": "valid_3", + "instance": {}, + "valid": true + }, + { + "label": "invalid_1", + "instance": "hello", + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "oneof-overlap-limited", + "generatorExpectation": "may-fail-explicitly", + "featureTags": [ + "oneOf", + "overlap" + ], + "schema": { + "oneOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "integer", + "maximum": 10 + } + ] + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": -1, + "valid": true + }, + { + "label": "valid_2", + "instance": 11, + "valid": true + }, + { + "label": "invalid_1", + "instance": 0, + "valid": false + }, + { + "label": "invalid_2", + "instance": 5, + "valid": false + }, + { + "label": "invalid_3", + "instance": 10, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "object-number-key-strings", + "generatorExpectation": "must-generate", + "featureTags": [ + "object", + "exactKeys" + ], + "schema": { + "type": "object", + "properties": { + "0": { + "type": "integer" + }, + "00": { + "type": "string" + } + }, + "required": [ + "0", + "00" + ], + "additionalProperties": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "0": 1, + "00": "ok" + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "0": 1 + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "mincontains-with-prefixitems", + "generatorExpectation": "must-generate", + "featureTags": [ + "prefixItems", + "contains", + "minContains", + "maxContains" + ], + "schema": { + "type": "array", + "prefixItems": [ + { + "type": "number" + } + ], + "contains": { + "type": "integer" + }, + "minContains": 1, + "maxContains": 1, + "minItems": 1 + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": [ + 1.5, + 1 + ], + "valid": true + }, + { + "label": "valid_2", + "instance": [ + 1 + ], + "valid": true + }, + { + "label": "invalid_1", + "instance": [], + "valid": false + }, + { + "label": "invalid_2", + "instance": [ + 1, + 2 + ], + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G4" + }, + { + "id": "required-with-additional-properties-false", + "generatorExpectation": "must-generate", + "featureTags": [ + "additionalProperties" + ], + "schema": { + "type": "object", + "properties": { + "x": { + "type": "integer" + } + }, + "required": [ + "x" + ], + "additionalProperties": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": { + "x": 0 + }, + "valid": true + }, + { + "label": "invalid_1", + "instance": { + "x": 0, + "extra": true + }, + "valid": false + } + ], + "notes": "", + "acceptanceGate": "G3" + }, + { + "id": "array-mixed-evaluation", + "generatorExpectation": "must-generate", + "featureTags": [ + "unevaluatedItems", + "prefixItems" + ], + "schema": { + "type": "array", + "prefixItems": [ + { + "type": "integer" + } + ], + "unevaluatedItems": false + }, + "validation": { + "formatAssertion": false + }, + "resources": {}, + "witnesses": [ + { + "label": "valid_1", + "instance": [ + 1 + ], + "valid": true + }, + { + "label": "valid_2", + "instance": [], + "valid": true + }, + { + "label": "invalid_1", + "instance": [ + 1, + 2 + ], + "valid": false + } + ], + "notes": "[] also satisfies this schema; oracle negative list should not include []", + "acceptanceGate": "G4" + } + ] +} diff --git a/Tests/JSONInspectionTests/SnapshotPromotionTests.swift b/Tests/JSONInspectionTests/SnapshotPromotionTests.swift new file mode 100644 index 0000000..9823dae --- /dev/null +++ b/Tests/JSONInspectionTests/SnapshotPromotionTests.swift @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: Apache-2.0 +import Foundation +import Synchronization +import Testing +import JSON +import JSONNavigation +import JSONInspection + +// Independent NAV6 packet oracle; no expected topology is generated by the walker. +@Test func snapshotV12IndependentMixedOracleMatchesEveryRecord() throws { + let text = "{\n \"\": {\"0\": [null, {\"id\": 9007199254740993, \"amount\": 0.0100}]},\n \"0\": [\"numeric object key, not index\"],\n \"é\": \"composed\",\n \"é\": \"decomposed\",\n \"rows\": [\n {\"z\": null, \"a\": 1},\n {\"a\": 2},\n {},\n {\"b\": 3, \"a\": 4},\n false\n ],\n \"matrix\": [[0.1, 2], [3]]\n}\n" + let result = try JSONStructuralSnapshotBuilder(document: JSONParser().parseDocument(Data(text.utf8))).build().result + let expected: [(Int?, JSONLocationComponent?, JSONTraversalKind, Int, Int, Int)] = [ + (nil, nil, .object, 0, 6, 28), + (0, .key(""), .object, 1, 1, 7), + (1, .key("0"), .array, 2, 2, 7), + (2, .index(0), .null, 3, 0, 4), + (2, .index(1), .object, 3, 2, 7), + (4, .key("id"), .number, 4, 0, 6), + (4, .key("amount"), .number, 4, 0, 7), + (0, .key("0"), .array, 1, 1, 9), + (7, .index(0), .string, 2, 0, 9), + (0, .key("é"), .string, 1, 0, 10), + (0, .key("é"), .string, 1, 0, 11), + (0, .key("rows"), .array, 1, 5, 22), + (11, .index(0), .object, 2, 2, 15), + (12, .key("z"), .null, 3, 0, 14), + (12, .key("a"), .number, 3, 0, 15), + (11, .index(1), .object, 2, 1, 17), + (15, .key("a"), .number, 3, 0, 17), + (11, .index(2), .object, 2, 0, 18), + (11, .index(3), .object, 2, 2, 21), + (18, .key("b"), .number, 3, 0, 20), + (18, .key("a"), .number, 3, 0, 21), + (11, .index(4), .boolean, 2, 0, 22), + (0, .key("matrix"), .array, 1, 2, 28), + (22, .index(0), .array, 2, 2, 26), + (23, .index(0), .number, 3, 0, 25), + (23, .index(1), .number, 3, 0, 26), + (22, .index(1), .array, 2, 1, 28), + (26, .index(0), .number, 3, 0, 28), + ] + #expect(result.recordCount == expected.count) + for ordinal in expected.indices { + let record = try result.record(at: ordinal), want = expected[ordinal] + #expect(record.ordinal == ordinal && record.parentOrdinal == want.0) + #expect(record.componentFromParent == want.1 && record.kind == want.2) + #expect(record.depth == want.3 && record.childCount == want.4 && record.subtreeEndExclusive == want.5) + } +} + +private enum TransferMarker: Error { case afterTransfer } +private struct TransferProbe: JSONTraversalOwnedReducer { + enum Mode: Sendable { case twice, work, caller, cancellation } + let mode: Mode + mutating func observe(_ hit: JSONTraversalHit, value: JSON, + context: inout JSONTraversalContext) throws -> JSONTraversalAction { .continue } + mutating func finish(_ summary: JSONTraversalSummary, + context: inout JSONTraversalContext) throws -> JSONDocument { + let document = try context.transferDocumentOwnership() + switch mode { + case .twice: + do { _ = try context.transferDocumentOwnership() } catch { } + case .work: + do { try context.work(64_000_000) } catch { } + case .cancellation: + withUnsafeCurrentTask { $0?.cancel() } + do { try context.checkpoint() } catch { } + case .caller: throw TransferMarker.afterTransfer + } + // Success/replacement cannot mask a terminal failure latched after transfer. + if mode == .twice { return document } + throw TransferMarker.afterTransfer + } +} + +@Test func snapshotV20TransferFailureCannotPublishAndScopeAlwaysInvalidates() async throws { + let document = try JSONParser().parseDocument(Data("[null]".utf8)) + for mode in [TransferProbe.Mode.twice, .work, .caller, .cancellation] { + let invalidated = Mutex(false) + let task = Task { @Sendable [document, mode] in + var walk = JSONTraversal(document: document) + walk.lifetimeObserver = { rootGone, stateGone in invalidated.withLock { $0 = rootGone && stateGone } } + var probe = TransferProbe(mode: mode) + return try walk.inspectOwned(reducer: &probe) + } + do { _ = try await task.value; Issue.record("No post-transfer failure may publish a report") } + catch let error as JSONFailure { + if mode == .twice { #expect(error.category == .configuration && error.code == .invalidOperation) } + else { #expect(mode == .work && error.resource?.metric == .workUnits) } + } + catch is CancellationError { #expect(mode == .cancellation) } + catch is TransferMarker { #expect(mode == .caller) } + #expect(invalidated.withLock { $0 }) + } + // Ordinary read/visit/N2 have no ownership transfer: their refunds are unchanged. + #expect(try document.readWithReport { _ in true }.statistics[.retainedBytes] == 0) + #expect(try JSONTraversal(document: document).visit { _, _ in .continue }.statistics[.retainedBytes] == 0) + let shape = try JSONShapeInspector(document: document).inspectArray() + #expect(shape.statistics[.retainedBytes] == 256) +} + +@Test func snapshotV22ConcurrentLookupsUseIndependentAdmissionAndHealth() async throws { + let document = try JSONParser().parseDocument(Data(#"{"x":[null,9007199254740993]}"#.utf8)) + let snapshot = try JSONStructuralSnapshotBuilder(document: document).build().result + let baseline = try snapshot.value(for: 3) + try await withThrowingTaskGroup(of: UInt64.self) { group in + for ordinal in 0..<16 { group.addTask { + if ordinal.isMultiple(of: 2) { + var limits = JSONLimits.balanced; limits[.nodes] = 0 + do { _ = try snapshot.value(for: 3, limits: limits); Issue.record("Expected source-admission refusal") } + catch let error as JSONFailure { #expect(error.resource?.metric == .nodes) } + } + let value = try snapshot.value(for: 3) + #expect(value.result == baseline.result) + #expect(value.statistics[.nodes] == UInt64(snapshot.recordCount)) + #expect(try snapshot.location(for: 3).result == [.key("x"), .index(1)]) + #expect(try snapshot.sourceSpan(for: 3).result != nil) + return value.statistics[.workUnits] + } } + for try await work in group { #expect(work == baseline.statistics[.workUnits]) } + } +} diff --git a/Tests/JSONSchemaExamplesTests/AdvancedConstructionTests.swift b/Tests/JSONSchemaExamplesTests/AdvancedConstructionTests.swift new file mode 100644 index 0000000..b9eec56 --- /dev/null +++ b/Tests/JSONSchemaExamplesTests/AdvancedConstructionTests.swift @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: Apache-2.0 +import Foundation +import JSON +import JSONSchema +@testable import JSONSchemaExamples +import Testing + +private func prepared(_ source: String, options: JSONSchemaExampleOptions = .init()) throws -> JSONSchemaExampleGenerator { + try .init(document: .init(JSONParser().parse(source)), options: options) +} +private func cases() throws -> [JSON] { + let url = URL(fileURLWithPath: #filePath).deletingLastPathComponent().deletingLastPathComponent() + .appendingPathComponent("Fixtures/SchemaExamples/S3-integral-correction-v1.json") + return try #require(JSONParser().parse(Data(contentsOf: url)).objectValue?["cases"]?.arrayValue) +} +private func registry(_ c: JSONObject) throws -> JSONSchemaRegistry { + var documents: [JSONSchemaDocument] = [] + if let resources = c["resources"]?.objectValue { + for member in resources { documents.append(try .init(member.value, retrievalURI: JSONSchemaURI(member.key.string))) } + } + return try .init(documents: documents) +} +private func resource(_ error: any Error, _ metric: JSONResourceMetric) -> Bool { + guard let error = error as? JSONFailure else { return false } + return error.category == .resource && error.resource?.metric == metric +} + +@Test func g4AllTenRequiredAndEightIncompleteFixturesHaveHonestNativeOutcomes() throws { + var required: [String] = [], incomplete: [String] = [], successes = 0, failures = 0, witnesses = 0, mustSuccesses = 0 + for value in try cases() { + let c = try #require(value.objectValue) + guard c["acceptanceGate"] == .string("G4") else { continue } + let id = try #require(c["id"]?.stringValue) + let must = c["generatorExpectation"] == .string("must-generate") + let options = JSONSchemaExampleOptions(validationOptions: .init(formatAssertion: c["validation"]?.objectValue?["formatAssertion"]?.booleanValue == true)) + let generator = try JSONSchemaExampleGenerator(document: .init(try #require(c["schema"])), registry: registry(c), options: options) + let plan = try JSONSchemaCompiler(options: options.compilationOptions).compile(generator.document, registry: generator.registry) + let validator = JSONSchemaValidator(options: options.validationOptions) + for witness in try #require(c["witnesses"]?.arrayValue) { + #expect(try validator.validate(#require(witness.objectValue?["instance"]), against: plan).isValid == #require(witness.objectValue?["valid"]?.booleanValue as Bool?)) + witnesses += 1 + } + var valid = 0, unsupported = 0, exhausted = 0 + for profile in [JSONSchemaExampleProfile.minimal, .demonstration] { + for seed in [UInt64(0),1,42,UInt64.max] { + do { + let result = try generator.generate(seed: seed, profile: profile) + #expect(result.validation.isValid && result.validation.diagnosticsComplete) + #expect(try validator.validate(result.value, against: plan).isValid) + #expect(try JSONParser().parse(JSONWriter().write(result.value)) == result.value) + #expect(result.statistics[.workUnits] <= options.limits[.workUnits]) + successes += 1; valid += 1; if must { mustSuccesses += 1 } + } catch let failure as JSONSchemaExampleFailure { + #expect(!must, "Required G4 fixture failed: \(id)") + switch failure { + case .unsupportedFeature: unsupported += 1 + case .searchExhausted(let attempts): #expect(attempts > 0 && attempts <= options.maximumAttempts); exhausted += 1 + default: Issue.record("Incomplete search became a proof/configuration error: \(id)") + } + failures += 1 + } + // Native operational errors/cancellation escape and fail the test. + } + } + if must { required.append(id) } else { incomplete.append(id) } + print("G4_FIXTURE id=\(id) must=\(must) valid=\(valid) unsupported=\(unsupported) exhausted=\(exhausted)") + } + #expect(required == ["array-contains","condition-then-else","dependent-required","dependent-schema","pattern-properties","property-names","unevaluated-properties-composition","simple-regex","mincontains-with-prefixitems","array-mixed-evaluation"]) + #expect(incomplete == ["oneof-overlap","recursive-tree","dynamic-ref","conflicting-allof","unfulfillable-unique-enum","external-offline-ref","not-string","oneof-overlap-limited"]) + #expect(mustSuccesses == 80 && successes + failures == 144 && witnesses == 59) + print("G4_ACCEPTANCE passed required=10 incomplete=8 must_valid=80 valid=\(successes) explicit=\(failures) witnesses=59") +} + +@Test func g4AnchoredLiteralRangeAndExactRepeatProposalsAreNativeCertified() throws { + for (pattern, expected) in [("^SKU-[0-9]{4}$","SKU-0000"), ("^[A-C]{2}X$","AAX"), (#"^a\+b\?$"#,"a+b?"), ("^$","")] { + let raw = try JSONObject(members: [.init("type",value: .string("string")),.init("pattern",value: .string(pattern))]) + let result = try JSONSchemaExampleGenerator(document: .init(.object(raw))).generate() + #expect(result.value == .string(expected) && result.validation.isValid) + } + var meter = try JSONWorkMeter(limits: .balanced, domain: .core) + #expect(try SchemaExamplePattern.propose("^[A-C]{2}X$", variation: 2, minimum: 0, maximum: 4, meter: &meter) == "CCX") +} + +@Test func g4UnsupportedRegexSyntaxNeverPretendsToBeAnInverter() throws { + var count = 0 + for pattern in ["SKU-[0-9]{4}","^(a|b)$","^(a+)+$",#"^(a)\1$"#,"^(?=a)a$","^[^a]$","^[ab]$","^a{1,3}$","^a?$","^é$"] { + var meter = try JSONWorkMeter(limits: .balanced, domain: .core) + #expect(try SchemaExamplePattern.propose(pattern, variation: 0, minimum: 0, maximum: 1024, meter: &meter) == nil) + #expect(meter.used(.workUnits) > 0); count += 1 + } + #expect(count == 10) +} + +@Test func g4UnsupportedPatternsCanStillUseAdmittedCorpusWithoutFallbackEngine() throws { + let source = #"{"type":"string","pattern":"^(ab)+$"}"# + #expect(throws: JSONSchemaExampleFailure.searchExhausted(attempts: 3)) { try prepared(source, options: .init(maximumAttempts: 3)).generate() } + let generated = try prepared(source, options: .init(corpus: .init(values: [.string("ab")]))).generate(profile: .demonstration) + #expect(generated.value == .string("ab") && generated.validation.isValid) + let unicode = try prepared(#"{"type":"string","pattern":"^é$"}"#, options: .init(corpus: .init(values: [.string("é")]))).generate(profile: .demonstration) + #expect(unicode.value == .string("é") && unicode.validation.isValid) + let plan = try JSONSchemaCompiler().compile(JSONSchemaDocument(JSONParser().parse(#"{"pattern":"^é$"}"#))) + #expect(try !JSONSchemaValidator().validate(.string("e\u{301}"), against: plan).isValid) +} + +@Test func g4ConjunctivePatternsAreNeverOverwrittenOrLocallyCertified() throws { + let conflict = #"{"type":"string","allOf":[{"pattern":"^A[0-9]$"},{"pattern":"^B[0-9]$"}]}"# + #expect(throws: JSONSchemaExampleFailure.searchExhausted(attempts: 3)) { try prepared(conflict, options: .init(maximumAttempts: 3)).generate() } + let compatible = #"{"type":"string","allOf":[{"pattern":"^A[0-9]$"},{"pattern":"^A0$"}]}"# + #expect(try prepared(compatible).generate().value == .string("A0")) +} + +@Test func g4ContainsDefaultZeroAndInactiveSiblingLimitsRemainDistinct() throws { + #expect(try prepared(#"{"type":"array","items":{"type":"integer"},"contains":{"multipleOf":2}}"#).generate().value == .array([0])) + #expect(try prepared(#"{"type":"array","contains":false,"minContains":0,"maxContains":0}"#).generate().value == .array([])) + #expect(try prepared(#"{"type":"array","minContains":1000,"maxContains":0}"#).generate().value == .array([])) +} + +@Test func g4ContainsPrefixAndUniqueConstraintsShareTheRootCertificate() throws { + let source = #"{"type":"array","prefixItems":[{"const":2}],"items":{"type":"integer"},"minItems":2,"uniqueItems":true,"contains":{"multipleOf":2},"minContains":2,"maxContains":2}"# + let result = try prepared(source).generate() + #expect(result.value == .array([2,4]) && result.attempts == 2 && result.validation.isValid) + let conjuncts = #"{"type":"array","items":{"type":"integer"},"allOf":[{"contains":{"minimum":2},"minContains":2},{"contains":{"multipleOf":2},"minContains":2}]}"# + #expect(try prepared(conjuncts).generate().value == .array([2,2])) + let evaluated = #"{"type":"array","contains":{"const":1},"minContains":2,"unevaluatedItems":false}"# + #expect(try prepared(evaluated).generate().value == .array([1,1])) +} + +@Test func g4ExtraContainsMatchesRemainHonestSearchExhaustion() throws { + let source = #"{"type":"array","minItems":3,"items":{"const":2},"contains":{"type":"integer"},"minContains":1,"maxContains":1}"# + #expect(throws: JSONSchemaExampleFailure.searchExhausted(attempts: 3)) { try prepared(source, options: .init(maximumAttempts: 3)).generate() } + let falseContains = #"{"type":"array","contains":false}"# + #expect(throws: JSONSchemaExampleFailure.unsupportedFeature(.candidateConstruction)) { try prepared(falseContains).generate() } + let unevaluated = #"{"type":"array","prefixItems":[{"const":2}],"minItems":2,"unevaluatedItems":false}"# + #expect(throws: JSONSchemaExampleFailure.searchExhausted(attempts: 3)) { try prepared(unevaluated, options: .init(maximumAttempts: 3)).generate() } +} + +@Test func g4ContainsCardinalityIsCheckedBeforeItemStaging() throws { + var limits = JSONLimits.balanced; limits[.arrayItems] = 1 + do { _ = try prepared(#"{"type":"array","contains":true,"minContains":2}"#, options: .init(limits: limits)).generate(); Issue.record("Contains count bypassed array cap") } + catch { #expect(resource(error,.arrayItems)) } +} + +@Test func g4RegexOperationalFailureIsTerminalRatherThanAnInvalidCandidate() throws { + do { _ = try prepared(#"{"pattern":"["}"#); Issue.record("Malformed regex reached proposal construction") } + catch let failure as JSONSchemaCompilationFailure { #expect(failure.category == .schemaSyntax) } + var limits = JSONLimits.balanced; limits[.regexSearchWorkUnits] = 0 + do { _ = try prepared(#"{"type":"string","pattern":"^SKU-[0-9]{4}$"}"#, options: .init(validationOptions: .init(limits: limits))).generate(); Issue.record("Regex operation retried or certified") } + catch { #expect(resource(error,.regexSearchWorkUnits)) } + limits = .balanced; limits[.regexCompileWorkUnits] = 0 + do { _ = try prepared(#"{"pattern":"a"}"#, options: .init(compilationOptions: .init(limits: limits))); Issue.record("Regex compile error became search failure") } + catch { #expect(resource(error,.regexCompileWorkUnits)) } +} + +@Test func g4PatternScannerHasExactWorkAndPreallocationBoundaries() throws { + var measure = try JSONWorkMeter(limits: .balanced, domain: .core) + #expect(try SchemaExamplePattern.propose("^SKU-[0-9]{4}$", variation: 0, minimum: 0, maximum: 100, meter: &measure) == "SKU-0000") + var limits = JSONLimits.balanced; limits[.workUnits] = measure.used(.workUnits) + var exact = try JSONWorkMeter(limits: limits, domain: .core) + #expect(try SchemaExamplePattern.propose("^SKU-[0-9]{4}$", variation: 0, minimum: 0, maximum: 100, meter: &exact) == "SKU-0000") + limits[.workUnits] -= 1 + var short = try JSONWorkMeter(limits: limits, domain: .core) + do { _ = try SchemaExamplePattern.propose("^SKU-[0-9]{4}$", variation: 0, minimum: 0, maximum: 100, meter: &short); Issue.record("Scanner work reset") } + catch { #expect(resource(error,.workUnits)) } + for metric in [JSONResourceMetric.stringBytes,.regexPatternBytes] { + limits = .balanced; limits[metric] = 3 + var meter = try JSONWorkMeter(limits: limits, domain: .core) + do { _ = try SchemaExamplePattern.propose("^A{4}$", variation: 0, minimum: 0, maximum: 100, meter: &meter); Issue.record("Scanner growth cap ignored") } + catch { #expect(resource(error,metric)) } + } +} + +@Test func g4CancellationDuringPatternScanningLatchesTheInvocation() async throws { + let task = Task { + var limits = JSONLimits.balanced; limits[.cancellationQuantum] = 1 + var meter = try JSONWorkMeter(limits: limits, domain: .core) + meter.checkpointObserver = { work in if work >= 10 { withUnsafeCurrentTask { $0?.cancel() } } } + #expect(throws: CancellationError.self) { try SchemaExamplePattern.propose("^" + String(repeating: "a",count: 256) + "$", variation: 0, minimum: 0, maximum: 1024, meter: &meter) } + #expect(throws: CancellationError.self) { try meter.work() } + } + try await task.value +} + +@Test func g4ConditionalFalseVerdictConsumesTheCumulativeEvaluationBudget() throws { + let c = try #require(cases().first { $0.objectValue?["id"] == .string("condition-then-else") }?.objectValue) + let document = try JSONSchemaDocument(#require(c["schema"])), generator = try JSONSchemaExampleGenerator(document: document) + let result = try generator.generate(), plan = try JSONSchemaCompiler().compile(document) + let rejected = try JSONSchemaValidator().validateWithReport(JSONParser().parse(#"{"age":0,"kind":"adult"}"#), against: plan) + let accepted = try JSONSchemaValidator().validateWithReport(result.value, against: plan) + #expect(!rejected.result.isValid && accepted.result.isValid && result.attempts == 2) + #expect(result.value.objectValue?["kind"] == .string("child")) + #expect(result.statistics[.evaluationWorkUnits] == rejected.statistics[.workUnits] + accepted.statistics[.workUnits]) + var limits = JSONLimits.balanced; limits[.evaluationWorkUnits] = result.statistics[.evaluationWorkUnits] + #expect(try JSONSchemaExampleGenerator(document: document, options: .init(validationOptions: .init(limits: limits))).generate().validation.isValid) + limits[.evaluationWorkUnits] -= 1 + do { _ = try JSONSchemaExampleGenerator(document: document, options: .init(validationOptions: .init(limits: limits))).generate(); Issue.record("False conditional verdict work refunded") } + catch { #expect(resource(error,.evaluationWorkUnits)) } +} + +@Test func g4ExternalReferencesKeepResourceBasesAndAnchorsQualified() throws { + let a = try JSONSchemaDocument(JSONParser().parse(##"{"$id":"urn:catalog:a","$defs":{"item":{"$anchor":"entry","const":"A"}}}"##), retrievalURI: JSONSchemaURI("urn:catalog:a")) + let b = try JSONSchemaDocument(JSONParser().parse(##"{"$id":"urn:catalog:b","$defs":{"item":{"$id":"urn:catalog:nested","$anchor":"entry","const":"B"}}}"##), retrievalURI: JSONSchemaURI("urn:catalog:b")) + let root = try JSONSchemaDocument(JSONParser().parse(##"{"type":"object","required":["a","b"],"properties":{"a":{"$ref":"urn:catalog:a#entry"},"b":{"$ref":"urn:catalog:nested#entry"}}}"##)) + let generator = try JSONSchemaExampleGenerator(document: root, registry: .init(documents: [a,b])) + let result = try generator.generate() + #expect(result.value.objectValue?["a"] == .string("A") && result.value.objectValue?["b"] == .string("B")) + #expect(result.validation.isValid) +} + +@Test func g4MissingOfflineResourceRemainsACompilationError() throws { + do { _ = try prepared(#"{"$ref":"urn:not-supplied"}"#); Issue.record("Missing resource became candidate search") } + catch let failure as JSONSchemaCompilationFailure { #expect(failure.category == .missingResource) } +} + +@Test func g4ReachedDynamicScopeIsUnsupportedAndRecursiveGrowthIsBounded() throws { + let dynamic = ##"{"$dynamicAnchor":"node","type":"array","minItems":1,"items":{"$dynamicRef":"#node"}}"## + #expect(throws: JSONSchemaExampleFailure.unsupportedFeature(.dynamicReference)) { try prepared(dynamic).generate() } + var limits = JSONLimits.balanced; limits[.maxDepth] = 8 + let recursive = ##"{"type":"array","minItems":1,"items":{"$ref":"#"}}"## + do { _ = try prepared(recursive, options: .init(limits: limits)).generate(); Issue.record("Recursive instance growth escaped depth cap") } + catch { #expect(resource(error,.maxDepth)) } +} + +@Test func g4IncompletenessNeverBecomesAnUnsatisfiabilityProof() throws { + let overlap = try prepared(#"{"oneOf":[{"type":"number"},{"type":"integer"}]}"#).generate() + #expect(overlap.value == .number(try JSONNumber(parsing: "0.1")) && overlap.attempts == 3) + #expect(throws: JSONSchemaExampleFailure.unsupportedFeature(.candidateConstruction)) { try prepared(#"{"not":{"type":"string"}}"#).generate() } + #expect(throws: JSONSchemaExampleFailure.unsupportedFeature(.candidateConstruction)) { try prepared(#"{"allOf":[{"type":"string"},{"type":"integer"}]}"#).generate() } + #expect(throws: JSONSchemaExampleFailure.searchExhausted(attempts: 3)) { try prepared(#"{"type":"array","minItems":3,"maxItems":3,"uniqueItems":true,"items":{"enum":["a","b"]}}"#, options: .init(maximumAttempts: 3)).generate() } + let triggered = #"{"type":"object","required":["a"],"dependentRequired":{"a":["b"]}}"# + #expect(throws: JSONSchemaExampleFailure.searchExhausted(attempts: 3)) { try prepared(triggered, options: .init(maximumAttempts: 3)).generate() } +} + +@Test func g4SharedAdvancedGeneratorHasIndependentConcurrentInvocations() async throws { + let generator = try prepared(#"{"type":"array","minItems":2,"uniqueItems":true,"items":{"type":"string"},"contains":{"pattern":"^SKU-[0-9]{4}$"},"minContains":2,"maxContains":2}"#) + let expected = try generator.generate(seed: 42, profile: .demonstration) + try await withThrowingTaskGroup(of: JSONSchemaExampleResult.self) { group in + for _ in 0..<32 { group.addTask { try generator.generate(seed: 42, profile: .demonstration) } } + var count = 0 + for try await result in group { + #expect(result.value == expected.value && result.traceDigest == expected.traceDigest && result.statistics == expected.statistics) + count += 1 + } + #expect(count == 32) + } +} diff --git a/Tests/JSONSchemaExamplesTests/DeterminismTests.swift b/Tests/JSONSchemaExamplesTests/DeterminismTests.swift new file mode 100644 index 0000000..8ce7924 --- /dev/null +++ b/Tests/JSONSchemaExamplesTests/DeterminismTests.swift @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSON +import JSONSchema +@testable import JSONSchemaExamples +import Testing + +private func prepared(_ text: String, options: JSONSchemaExampleOptions = .init()) throws -> JSONSchemaExampleGenerator { + try .init(document: JSONSchemaDocument(JSONParser().parse(text)), options: options) +} +private func testTrace(meter: inout JSONWorkMeter) throws -> SchemaExampleTrace { + try .init(snapshot: "s", corpus: "c", policy: "p", seed: 3, profile: .demonstration, meter: &meter) +} +private func resource(_ error: any Error, _ metric: JSONResourceMetric) -> Bool { + guard let error = error as? JSONFailure else { return false } + return error.category == .resource && error.resource?.metric == metric +} + +@Test func g2MeteredSplitMixMatchesAllReferenceGoldenDraws() throws { + let vectors: [(UInt64, [UInt64])] = [ + (0, [0xe220a8397b1dcdaf, 0x6e789e6aa1b965f4, 0x06c45d188009454f, 0xf88bb8a8724c81ec]), + (1, [0x910a2dec89025cc1, 0xbeeb8da1658eec67, 0xf893a2eefb32555e, 0x71c18690ee42c90b]), + (42, [0xbdd732262feb6e95, 0x28efe333b266f103, 0x47526757130f9f52, 0x581ce1ff0e4ae394]), + (UInt64.max, [0xe4d971771b652c20, 0xe99ff867dbf682c9, 0x382ff84cb27281e9, 0x6d1db36ccba982d2])] + for (seed, expected) in vectors { + var meter = try JSONWorkMeter(limits: .balanced, domain: .core) + var rng = SchemaExampleRandom(seed: seed) + for golden in expected { #expect(try rng.next(meter: &meter) == golden) } + #expect(rng.draws == 4 && meter.used(.workUnits) == 40) + } +} + +@Test func g2UniformSelectionsStayInRangeAtUInt64Boundaries() throws { + for bound in [UInt64(1), 2, 7, 128, UInt64.max] { + var meter = try JSONWorkMeter(limits: .balanced, domain: .core) + var trace = try testTrace(meter: &meter), rng = SchemaExampleRandom(seed: 42) + for _ in 0..<128 { #expect(try rng.uniform(upperBound: bound, trace: &trace, meter: &meter) < bound) } + #expect(trace.events == rng.draws && rng.draws >= 128) + } +} + +@Test func g2RejectedUniformDrawsChargeWorkAndTraceBeforeRetry() throws { + var meter = try JSONWorkMeter(limits: .balanced, domain: .core) + var trace = try testTrace(meter: &meter), rng = SchemaExampleRandom(seed: 3) + let before = meter.used(.workUnits), diagnosticBefore = meter.used(.diagnosticBytes) + let selected = try rng.uniform(upperBound: 0x8000000000000001, trace: &trace, meter: &meter) + #expect(selected == 0x33466f8a7b81a988 && rng.draws == 2 && trace.events == 2) + #expect(meter.used(.workUnits) - before == 3 + 2 * 29) + #expect(meter.used(.diagnosticBytes) - diagnosticBefore == 34) +} + +@Test func g2UniformZeroBoundFailsAndLatchesTheLedger() throws { + var meter = try JSONWorkMeter(limits: .balanced, domain: .core) + var trace = try testTrace(meter: &meter), rng = SchemaExampleRandom(seed: 3) + let failure = JSONFailure(.configuration, code: .invalidOperation) + #expect(throws: failure) { try rng.uniform(upperBound: 0, trace: &trace, meter: &meter) } + #expect(throws: failure) { try rng.next(meter: &meter) } + #expect(rng.draws == 0) +} + +@Test func g2WorkExhaustionAfterRejectedDrawCannotResetOrAdvanceRandomState() throws { + var measure = try JSONWorkMeter(limits: .balanced, domain: .core) + let measuredTrace = try testTrace(meter: &measure) + _ = consume measuredTrace + var limits = JSONLimits.balanced; limits[.workUnits] = measure.used(.workUnits) + 3 + 29 + 9 + var meter = try JSONWorkMeter(limits: limits, domain: .core) + var trace = try testTrace(meter: &meter), rng = SchemaExampleRandom(seed: 3) + do { _ = try rng.uniform(upperBound: 0x8000000000000001, trace: &trace, meter: &meter); Issue.record("Rejected-draw work cap ignored") } + catch { #expect(resource(error, .workUnits)) } + #expect(rng.draws == 1 && trace.events == 1) + do { _ = try rng.next(meter: &meter); Issue.record("Failed RNG ledger resumed") } + catch { #expect(resource(error, .workUnits)) } + #expect(rng.draws == 1) +} + +@Test func g2CancellationDuringRandomSearchIsTerminal() async throws { + let task = Task { + var limits = JSONLimits.balanced; limits[.cancellationQuantum] = 1 + var meter = try JSONWorkMeter(limits: limits, domain: .core) + var trace = try testTrace(meter: &meter), rng = SchemaExampleRandom(seed: 3) + let trigger = meter.used(.workUnits) + 3 + 5 + meter.checkpointObserver = { work in if work >= trigger { withUnsafeCurrentTask { $0?.cancel() } } } + do { _ = try rng.uniform(upperBound: 0x8000000000000001, trace: &trace, meter: &meter); Issue.record("Cancellation ignored") } + catch is CancellationError { } + #expect(throws: CancellationError.self) { try rng.next(meter: &meter) } + #expect(rng.draws == 0) + } + try await task.value +} + +@Test func g2ConstructionNormalizesOrderAndLexemesWithoutLosingExactValues() throws { + let a = try prepared(#"{"const":{"z":[[9007199254740993]],"é":1.0,"e\u0301":9007199254740994.5}}"#).generate(seed: 42) + let b = try prepared(#"{"const":{"e\u0301":9007199254740994.5,"é":1,"z":[[9007199254740993]]}}"#).generate(seed: 42) + let text = try JSONWriter().writeString(a.value) + #expect(text.utf8.elementsEqual(#"{"é":90071992547409945e-1,"z":[[9007199254740993]],"é":1}"#.utf8)) + #expect(try JSONWriter().write(b.value) == JSONWriter().write(a.value)) + #expect(a.sourceSnapshotDigest == b.sourceSnapshotDigest && a.traceDigest == b.traceDigest) + #expect(a.value.objectValue?.count == 3 && a.validation.isValid && b.validation.isValid) +} + +@Test func g2ConstructionUsesIterativeCodecForDeepNesting() throws { + var value: JSON = .number(try JSONNumber(parsing: "9007199254740993")) + for _ in 0..<100 { value = .array([value]) } + let document = try JSONSchemaDocument(.object(JSONObject(members: [.init("const", value: value)]))) + let result = try JSONSchemaExampleGenerator(document: document).generate() + #expect(result.value == value && result.validation.isValid) +} + +@Test func g2ConstructionChecksDepthAndNumericCapsBeforeCertification() throws { + var limits = JSONLimits.balanced; limits[.maxDepth] = 2 + do { _ = try prepared(#"{"const":[[[0]]]}"#, options: .init(limits: limits)).generate(); Issue.record("Depth cap ignored") } + catch { #expect(resource(error, .maxDepth)) } + limits = .balanced; limits[.numberDigits] = 3 + do { _ = try prepared(#"{"const":9007199254740993}"#, options: .init(limits: limits)).generate(); Issue.record("Numeric cap ignored") } + catch { #expect(resource(error, .numberDigits)) } +} + +@Test func g2CorpusIsAdmittedExactOrderedAndImmutable() throws { + let syntheticText = #"[null,false,0,"example",[],{}]"# + for _ in 0..<32 { + let corpus = JSONSchemaExampleCorpus.synthetic + #expect(try JSONWriter().writeString(.array(corpus.values)) == syntheticText) + var local = corpus.values + local.append(.string("caller-only")) + #expect(local.count == 7 && corpus.values.count == 6) + #expect(JSONSchemaExampleOptions().corpus.values.count == 6) + } + let one = try JSONParser().parse(#"{"é":1.0,"e\u0301":9007199254740993}"#) + let two = try JSONParser().parse(#"{"e\u0301":9007199254740993,"é":1}"#) + let a = try prepared("true", options: .init(corpus: .init(values: [one, .null]))) + let b = try prepared("true", options: .init(corpus: .init(values: [two, .null]))) + let reversed = try prepared("true", options: .init(corpus: .init(values: [.null, two]))) + #expect(a.corpusDigest == b.corpusDigest && a.corpusDigest != reversed.corpusDigest) + let result = try a.generate(seed: 2, profile: .demonstration) + #expect(result.value == one && result.value.objectValue?.count == 2 && result.validation.isValid) + #expect(result.corpusDigest == a.corpusDigest) +} + +@Test func g2EmptyOrOversizedCorpusFailsDuringPreparation() throws { + #expect(throws: JSONSchemaExampleFailure.invalidConfiguration(.emptyCorpus)) { + try prepared("true", options: .init(corpus: .init(values: []))) + } + var limits = JSONLimits.balanced; limits[.stringBytes] = 4096 + #expect(try prepared("true", options: .init(corpus: .init(values: [.null]), compilationOptions: .init(limits: limits))).generate().validation.isValid) + do { _ = try prepared("true", options: .init(corpus: .init(values: [.string(String(repeating: "x", count: 4097))]), compilationOptions: .init(limits: limits))); Issue.record("Corpus bypassed admission") } + catch { #expect(resource(error, .stringBytes)) } +} + +@Test func g2SnapshotCorpusAndPolicyMatchIndependentTransportGoldens() throws { + let g = try prepared("true") + #expect(g.sourceSnapshotDigest == "a69f2db52526c6c09592bc65478664deb370ef9d834aff678fa3f9879ae555aa") + #expect(g.corpusDigest == "c8cfba87987771a2ae30e1e9d9f345ea45cb7a1e93eb69a5e2a7643881bf6847") + #expect(g.policyDigest == "f9440e6af33f0e80bea2ae4dad4e532259bf722044d4ad0d925ae14d87ed3ff3") +} + +@Test func g2All49GenerationCapsContributeToPolicyIdentity() throws { + var meter = try JSONWorkMeter(limits: .offline, domain: .core) + let base = try SchemaExampleDigests.policy(.init(), meter: &meter) + var count = 0 + for metric in JSONResourceMetric.allCases { + var options = JSONSchemaExampleOptions(); options.limits[metric] -= 1 + #expect(try SchemaExampleDigests.policy(options, meter: &meter) != base) + count += 1 + } + #expect(count == 49) +} + +@Test func g2AllPolicyFlagsAndCompilationEvaluationCapsHaveDistinctIdentity() throws { + var meter = try JSONWorkMeter(limits: .offline, domain: .core) + let base = try SchemaExampleDigests.policy(.init(), meter: &meter) + for slot in 0..<11 { + var o = JSONSchemaExampleOptions() + switch slot { + case 0: o.maximumAttempts = 17 + case 1: o.compilationOptions.formatAssertion = true + case 2: o.compilationOptions.output = .flag + case 3: o.compilationOptions.diagnosticPolicy = .bestEffort + case 4: o.compilationOptions.collectAnnotations = true + case 5: o.validationOptions.formatAssertion = true + case 6: o.validationOptions.output = .flag + case 7: o.validationOptions.diagnosticPolicy = .bestEffort + case 8: o.validationOptions.collectAnnotations = true + case 9: o.compilationOptions.limits[.workUnits] -= 1 + default: o.validationOptions.limits[.workUnits] -= 1 + } + #expect(try SchemaExampleDigests.policy(o, meter: &meter) != base) + } +} + +@Test func g2CompleteTraceGoldensBindSeedProfileSourceCorpusAndPolicy() throws { + let g = try prepared("true"), minimal = try g.generate(seed: 42) + let demo = try g.generate(seed: 42, profile: .demonstration) + #expect(minimal.traceDigest == "b2fecfe6b1cbeeab34f0513708c31e19aa8e61ddf25d603447572b22d4fdd2f1") + #expect(demo.traceDigest == "21735803f7ea97cf7b18d6ab9b23c32fbc7b06ddc200878ae1ddb161bc5525c5") + #expect(minimal.randomDraws == 0 && minimal.traceEventCount == 4) + #expect(demo.randomDraws == 1 && demo.traceEventCount == 5 && demo.value == .boolean(false)) + #expect(minimal.schemaDigestAlgorithm == "swift-json-schema-snapshot-v1") + #expect(demo.rngAlgorithm == "splitmix64-v1" && demo.traceDigestAlgorithm == "swift-json-example-trace-v2") + #expect(try g.generate(seed: 43).traceDigest != minimal.traceDigest) + #expect(try prepared("{}").generate(seed: 42).traceDigest != minimal.traceDigest) + #expect(try prepared("true", options: .init(maximumAttempts: 17)).generate(seed: 42).traceDigest != minimal.traceDigest) + #expect(try prepared("true", options: .init(corpus: .init(values: [.null]))).generate(seed: 42).traceDigest != minimal.traceDigest) +} + +@Test func g2TraceBytesHaveExactFiniteBoundaryWithoutTruncation() throws { + var limits = JSONLimits.balanced; limits[.diagnosticBytes] = 344 + let exact = try prepared("true", options: .init(limits: limits)).generate(seed: 42) + #expect(exact.statistics[.diagnosticBytes] == 344 && exact.traceEventCount == 4) + limits[.diagnosticBytes] = 343 + do { _ = try prepared("true", options: .init(limits: limits)).generate(seed: 42); Issue.record("Truncated trace returned success") } + catch { #expect(resource(error, .diagnosticBytes)) } +} + +@Test func g2DemonstrationCircularScanKeepsRejectedNativeVerdictAndAttemptCeiling() throws { + let source = #"{"enum":[1,"ok"],"type":"string"}"# + let result = try prepared(source).generate(seed: 2, profile: .demonstration) + #expect(result.value == .string("ok") && result.attempts == 2 && result.randomDraws == 1 && result.traceEventCount == 7) + #expect(result.statistics[.evaluationWorkUnits] > 0) + #expect(throws: JSONSchemaExampleFailure.searchExhausted(attempts: 1)) { + try prepared(source, options: .init(maximumAttempts: 1)).generate(seed: 2, profile: .demonstration) + } +} + +@Test func g2KeywordDiscoveryIsIndependentOfObjectInsertionOrder() throws { + let a = try prepared(#"{"examples":["last"],"enum":["enum","default"],"default":"default","type":"string"}"#).generate() + let b = try prepared(#"{"type":"string","default":"default","enum":["enum","default"],"examples":["last"]}"#).generate() + #expect(a.value == .string("default") && a.attempts == 1) + #expect(a.sourceSnapshotDigest == b.sourceSnapshotDigest && a.traceDigest == b.traceDigest) +} + +@Test func g2TraceFinalizationIsSingleUse() throws { + var meter = try JSONWorkMeter(limits: .balanced, domain: .core) + var trace = try testTrace(meter: &meter) + try trace.event(4, 1, 1, meter: &meter) + #expect(try trace.digest(meter: &meter).count == 64) + let failure = JSONFailure(.configuration, code: .invalidOperation) + #expect(throws: failure) { try trace.event(5, 1, 0, meter: &meter) } + #expect(throws: failure) { try trace.digest(meter: &meter) } +} + +@Test func g2CancellationDuringExactConstructionCannotReturnACandidate() async throws { + let value: JSON = .array((0..<200).map { .string("token-\($0)") }) + let task = Task { + var meter = try JSONWorkMeter(limits: .balanced, domain: .core) + meter.checkpointObserver = { work in if work >= 100 { withUnsafeCurrentTask { $0?.cancel() } } } + #expect(throws: CancellationError.self) { try SchemaExampleExact.construct(value, meter: &meter) } + #expect(throws: CancellationError.self) { try meter.work() } + } + try await task.value +} + +@Test func g2StaticReferenceKindsUseStableScalarOrderWithOriginalRootValidation() throws { + let a = try prepared(##"{"$ref":"#/$defs/a","$dynamicRef":"#/$defs/b","$defs":{"a":{"enum":[1,"ok"]},"b":{"const":"ok"}}}"##).generate(seed: 42) + let b = try prepared(##"{"$defs":{"b":{"const":"ok"},"a":{"enum":[1,"ok"]}},"$dynamicRef":"#/$defs/b","$ref":"#/$defs/a"}"##).generate(seed: 42) + #expect(a.value == .string("ok") && a.attempts == 1 && a.validation.isValid) + #expect(a.sourceSnapshotDigest == b.sourceSnapshotDigest && a.traceDigest == b.traceDigest) +} diff --git a/Tests/JSONSchemaExamplesTests/LifecycleAndAccountingTests.swift b/Tests/JSONSchemaExamplesTests/LifecycleAndAccountingTests.swift new file mode 100644 index 0000000..8286f6b --- /dev/null +++ b/Tests/JSONSchemaExamplesTests/LifecycleAndAccountingTests.swift @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSON +import JSONSchema +@testable import JSONSchemaExamples +import Testing + +private func lifecycleGenerator(_ source: String, options: JSONSchemaExampleOptions = .init()) throws -> JSONSchemaExampleGenerator { + try .init(document: JSONSchemaDocument(JSONParser().parse(source)), options: options) +} +private func lifecycleMeter(_ options: JSONSchemaExampleOptions = .init()) throws -> JSONWorkMeter { + try .init(limits: options.limits.intersection(options.validationOptions.limits), domain: .core) +} +private func expectResource(_ error: any Error, metric: JSONResourceMetric, code: JSONFailure.Code = .limitExceeded) { + guard let failure = error as? JSONFailure else { Issue.record("Expected a native resource failure: \(error)"); return } + #expect(failure.category == .resource && failure.code == code && failure.resource?.metric == metric) +} + +@Test func g5TraceFinalizationReleasesOnlyItsActualTransportReservation() throws { + var meter = try lifecycleMeter() + try meter.retain(91) + var trace = try SchemaExampleTrace(snapshot: "s", corpus: "c", policy: "p", seed: 0, profile: .minimal, meter: &meter) + try trace.event(4, 1, 1, meter: &meter) + #expect(meter.used(.retainedBytes) == 91 + trace.retainedByteCount) + #expect(try trace.digest(meter: &meter).count == 64) + #expect(trace.retainedByteCount == 0 && meter.used(.retainedBytes) == 91 + 64) + // Misuse is terminal even when caught; unrelated work cannot resume. + let failure = JSONFailure(.configuration, code: .invalidOperation) + #expect(throws: failure) { try trace.event(1, 0, 0, meter: &meter) } + #expect(throws: failure) { try meter.work() } +} + +@Test func g5SuccessTransfersResultsAndReleasesInvocationReservations() throws { + var options = JSONSchemaExampleOptions(); options.validationOptions.collectAnnotations = true + let prepared = try lifecycleGenerator(#"{"type":"object","required":["a"],"properties":{"a":{"type":"integer","default":7}}}"#, options: options) + var meter = try lifecycleMeter(options) + try meter.retain(73); try meter.work(19) + let result = try prepared.generate(meter: &meter) + #expect(result.validation.isValid && result.value.objectValue?.count == 1) + #expect(result.statistics[.workUnits] == meter.used(.workUnits)) + #expect(result.statistics[.retainedBytes] > 73) + #expect(meter.used(.retainedBytes) == 73 && meter.used(.annotationBytes) == 0 && meter.used(.regexRetainedBytes) == 0) + try meter.work() + #expect(meter.used(.workUnits) == result.statistics[.workUnits] + 1) +} + +@Test func g5RepeatedSuccessDoesNotAccumulateLiveInvocationStateOrResetWork() throws { + let prepared = try lifecycleGenerator(#"{"enum":[0,"ok"],"type":"string"}"#) + let expected = try prepared.generate() + var meter = try lifecycleMeter() + for index: UInt64 in 1...12 { + let result = try prepared.generate(meter: &meter) + #expect(result.value == expected.value && result.traceDigest == expected.traceDigest) + #expect(meter.used(.retainedBytes) == 0 && meter.used(.annotationBytes) == 0) + #expect(meter.used(.workUnits) == expected.statistics[.workUnits] * index) + #expect(meter.used(.evaluationWorkUnits) == expected.statistics[.evaluationWorkUnits] * index) + } +} + +@Test func g5ReturnedCertificateOutlivesPreparedSourceAndRegistryScope() throws { + func scoped() throws -> JSONSchemaExampleResult { + let root = try JSONSchemaDocument(JSONParser().parse(##"{"$ref":"https://e.test/value"}"##)) + let external = try JSONSchemaDocument(JSONParser().parse(#"{"const":{"é":9007199254740993,"e\u0301":null}}"#), retrievalURI: JSONSchemaURI("https://e.test/value")) + return try JSONSchemaExampleGenerator(document: root, registry: .init(documents: [external])).generate() + } + let result = try scoped() + #expect(result.validation.isValid && result.value.objectValue?.count == 2) + #expect(try JSONParser().parse(JSONWriter().write(result.value)) == result.value) + let large = try JSONNumber(parsing: "9007199254740993") + #expect(result.value.objectValue?["é"]?.numberValue == large) +} + +@Test func g5InvalidVerdictsConsumeDiagnosticsAndAllCompletedEvaluationWork() throws { + let source = #"{"enum":[1,2,"ok"],"type":"string"}"# + let prepared = try lifecycleGenerator(source) + let result = try prepared.generate() + let plan = try JSONSchemaCompiler().compile(prepared.document) + var evaluation: UInt64 = 0, issues: UInt64 = 0, diagnostics: UInt64 = 0 + // Compare the same normalized parsed representation the generator certifies; + // native integer construction does not retain the parser's number lexeme. + for value in [try JSONParser().parse("1"), try JSONParser().parse("2"), .string("ok")] { + let report = try JSONSchemaValidator().validateWithReport(value, against: plan) + evaluation += report.statistics[.evaluationWorkUnits] + issues += report.statistics[.issues]; diagnostics += report.statistics[.diagnosticBytes] + } + #expect(result.attempts == 3 && result.statistics[.evaluationWorkUnits] == evaluation) + #expect(result.statistics[.issues] == issues && issues > 0) + #expect(result.statistics[.diagnosticBytes] > diagnostics) // Includes complete abstract trace. +} + +@Test func g5ExactCumulativeQuotaAndRetentionBoundaries() throws { + let source = #"{"default":0,"type":"string","pattern":"^[a-z]{3}$"}"# + let measured = try lifecycleGenerator(source).generate() + for metric: JSONResourceMetric in [.workUnits, .numericWorkUnits, .evaluationWorkUnits, .aggregateRegexWorkUnits, .diagnosticBytes, .issues, .retainedBytes] { + var options = JSONSchemaExampleOptions() + let cap = metric == .retainedBytes ? measured.statistics.peakLogicalRetainedBytes : measured.statistics[metric] + #expect(cap > 0) + options.limits[metric] = cap + let exact = try lifecycleGenerator(source, options: options).generate() + #expect(exact.validation.isValid) + options.limits[metric] = cap - 1 + var meter = try lifecycleMeter(options) + do { _ = try lifecycleGenerator(source, options: options).generate(meter: &meter); Issue.record("One-unit-short \(metric) returned a certificate") } + catch { expectResource(error, metric: metric) } + #expect(meter.used(.retainedBytes) == 0 && meter.used(.annotationBytes) == 0 && meter.used(.regexRetainedBytes) == 0) + let used = meter.statistics() + do { try meter.work(); Issue.record("Exhausted ledger resumed") } catch { expectResource(error, metric: metric) } + #expect(meter.statistics() == used) + } +} + +@Test func g5OperationalEvaluationFailureIsLatchedWithoutRetryOrReport() throws { + var options = JSONSchemaExampleOptions(); options.validationOptions.limits[.evaluationWorkUnits] = 0 + let prepared = try lifecycleGenerator(#"{"enum":[1,2,3]}"#, options: options) + var meter = try lifecycleMeter(options) + do { _ = try prepared.generate(meter: &meter); Issue.record("Operational failure returned success") } + catch { expectResource(error, metric: .evaluationWorkUnits) } + #expect(meter.domain == .schemaEvaluation && meter.used(.evaluationWorkUnits) == 0) + #expect(meter.used(.retainedBytes) == 0) + let stopped = meter.statistics() + do { _ = try prepared.generate(meter: &meter); Issue.record("Invocation restarted after error") } + catch { expectResource(error, metric: .evaluationWorkUnits) } + #expect(meter.statistics() == stopped) +} + +@Test func g5NativeNonproductiveReferenceFailureIsTerminalRatherThanSearchExhaustion() throws { + let prepared = try lifecycleGenerator(##"{"$ref":"#","default":0}"##) + var meter = try lifecycleMeter() + let expected = JSONSchemaEvaluationFailure(.nonproductiveReference) + #expect(throws: expected) { try prepared.generate(meter: &meter) } + #expect(meter.used(.retainedBytes) == 0 && meter.used(.annotationBytes) == 0) + let stopped = meter.statistics() + #expect(throws: expected) { try prepared.generate(meter: &meter) } + #expect(meter.statistics() == stopped) +} + +@Test func g5ExhaustedBranchesReleaseScratchWithoutClaimingUnsatisfiability() throws { + var options = JSONSchemaExampleOptions(maximumAttempts: 3) + options.validationOptions.collectAnnotations = true + let prepared = try lifecycleGenerator(#"{"type":"array","minItems":2,"maxItems":2,"uniqueItems":true,"items":{"enum":[0]}}"#, options: options) + var meter = try lifecycleMeter(options) + let expected = JSONSchemaExampleFailure.searchExhausted(attempts: 3) + #expect(throws: expected) { try prepared.generate(meter: &meter) } + #expect(meter.used(.evaluationWorkUnits) > 0 && meter.used(.issues) >= 3) + #expect(meter.used(.retainedBytes) == 0 && meter.used(.annotationBytes) == 0) + let stopped = meter.statistics() + #expect(throws: expected) { try prepared.generate(meter: &meter) } + #expect(meter.statistics() == stopped) +} + +@Test func g5NestedCyclicConstructionAndProofFailuresReleaseAndLatch() throws { + let cases: [(String, JSONSchemaExampleFailure)] = [ + (##"{"type":"object","required":["a"],"properties":{"a":{"$ref":"#/$defs/loop"}},"$defs":{"loop":{"allOf":[{"$ref":"#/$defs/loop"}]}}}"##, .unsupportedFeature(.cyclicReference)), + ("false", .provenUnsatisfiable(.falseSchema)), + (#"{"not":true}"#, .provenUnsatisfiable(.notTrue))] + for (source, expected) in cases { + let prepared = try lifecycleGenerator(source) + var meter = try lifecycleMeter() + #expect(throws: expected) { try prepared.generate(meter: &meter) } + #expect(meter.used(.retainedBytes) == 0 && meter.used(.regexRetainedBytes) == 0) + let stopped = meter.statistics() + #expect(throws: expected) { try meter.work() } + #expect(meter.statistics() == stopped) + } +} + +@Test func g5OverflowAndRetiredAdmissionCannotWrapOrRefundWork() throws { + var meter = try lifecycleMeter() + try meter.work(7) + do { _ = try meter.adding(.workUnits, UInt64.max, 1); Issue.record("Overflow admitted") } + catch { expectResource(error, metric: .workUnits, code: .counterOverflow) } + #expect(meter.used(.workUnits) == 7) + do { try meter.resetValueBounds(); try meter.work(); Issue.record("Overflow ledger resumed") } + catch { expectResource(error, metric: .workUnits, code: .counterOverflow) } + var retired = try lifecycleMeter() + try retired.work(11); try retired.charge(.nodes, 3); try retired.resetValueBounds() + try retired.charge(.nodes, 2) + #expect(retired.used(.workUnits) == 11 && retired.used(.nodes) == 2 && retired.statistics()[.nodes] == 5) +} + +@Test func g5CancellationAcrossDiscoveryConstructionEvaluationAndTraceIsTerminal() async throws { + let source = #"{"default":0,"type":"string","pattern":"^(a|aa)+b$","minLength":80,"maxLength":80}"# + var options = JSONSchemaExampleOptions(maximumAttempts: 2) + options.limits[.cancellationQuantum] = 1 + let prepared = try lifecycleGenerator(source, options: options) + // Each threshold is charged work, independent of clocks/scheduling. This + // reaches discovery, construction and native regex, then checks terminal state. + var observedRegexCancellation = false + for trigger: UInt64 in [1, 300, 900, 1800, 3000, 5000, 10000, 20000] { + let regex = try await Task { @Sendable [options, prepared, trigger] in + var meter = try lifecycleMeter(options) + meter.checkpointObserver = { work in if work >= trigger { withUnsafeCurrentTask { $0?.cancel() } } } + #expect(throws: CancellationError.self) { try prepared.generate(meter: &meter) } + #expect(meter.used(.retainedBytes) == 0 && meter.used(.annotationBytes) == 0 && meter.used(.regexRetainedBytes) == 0) + let stopped = meter.statistics() + #expect(throws: CancellationError.self) { try prepared.generate(meter: &meter) } + #expect(meter.statistics() == stopped) + return meter.domain == .schemaEvaluation && meter.used(.aggregateRegexWorkUnits) > 0 + }.value + observedRegexCancellation = observedRegexCancellation || regex + } + #expect(observedRegexCancellation) +} + +@Test func g5CancellationInsideLongDecimalDivisionLatchesItsSharedLedger() async throws { + let bound = try JSONNumber(parsing: String(repeating: "9", count: 128)) + let step = try JSONNumber(parsing: "7") + try await Task { + var options = JSONSchemaExampleOptions(); options.limits[.cancellationQuantum] = 1 + var meter = try lifecycleMeter(options) + meter.checkpointObserver = { work in if work >= 500 { withUnsafeCurrentTask { $0?.cancel() } } } + #expect(throws: CancellationError.self) { try SchemaExampleDecimal.rounded(bound, step: step, up: true, open: true, variation: 1, meter: &meter) } + #expect(meter.used(.numericWorkUnits) >= 500) + let stopped = meter.statistics() + #expect(throws: CancellationError.self) { try meter.work() } + #expect(meter.statistics() == stopped) + }.value +} + +@Test func g5CancellationDuringFinalTraceHashNeverPublishesACertificate() async throws { + let prepared = try lifecycleGenerator("true") + let measured = try prepared.generate() + let trigger = measured.statistics[.workUnits] - 1000 + try await Task { + var meter = try lifecycleMeter() + meter.checkpointObserver = { work in if work >= trigger { withUnsafeCurrentTask { $0?.cancel() } } } + #expect(throws: CancellationError.self) { try prepared.generate(meter: &meter) } + #expect(meter.domain == .core && meter.used(.evaluationWorkUnits) == measured.statistics[.evaluationWorkUnits]) + #expect(meter.used(.retainedBytes) == 0) + #expect(throws: CancellationError.self) { try meter.checkpoint() } + }.value + // Separate calls are independent; cancellation never mutates preparation. + #expect(try prepared.generate().traceDigest == measured.traceDigest) +} + +@Test func g5ZeroWorkAndPreparationQuotaFailureDoNotReturnPartialObjects() throws { + var options = JSONSchemaExampleOptions(); options.limits[.workUnits] = 0 + var meter = try lifecycleMeter(options) + do { _ = try lifecycleGenerator("true", options: options).generate(meter: &meter); Issue.record("Zero-work success") } + catch { expectResource(error, metric: .workUnits) } + #expect(meter.used(.workUnits) == 0 && meter.used(.retainedBytes) == 0) + options = .init() + let measured = try lifecycleGenerator("true").preparationStatistics[.workUnits] + options.compilationOptions.limits[.workUnits] = measured + #expect(try lifecycleGenerator("true", options: options).preparationStatistics[.workUnits] == measured) + options.compilationOptions.limits[.workUnits] -= 1 + do { _ = try lifecycleGenerator("true", options: options); Issue.record("Preparation quota reset for snapshot/corpus/policy") } + catch { expectResource(error, metric: .workUnits) } +} + +@Test func g5SharedPreparationSeparatesConcurrentFailureAndSuccessHealth() async throws { + let prepared = try lifecycleGenerator(#"{"type":"integer","minimum":9007199254740993}"#) + let expected = try prepared.generate() + try await withThrowingTaskGroup(of: Bool.self) { group in + for index in 0..<32 { + group.addTask { + if index.isMultiple(of: 2) { + withUnsafeCurrentTask { $0?.cancel() } + do { _ = try prepared.generate(); return false } catch is CancellationError { return true } + } + let result = try prepared.generate() + return result.value == expected.value && result.statistics == expected.statistics && result.traceDigest == expected.traceDigest + } + } + var count = 0 + for try await passed in group { #expect(passed); count += 1 } + #expect(count == 32) + } + #expect(try prepared.generate().value == expected.value) +} + +@Test func g5PolicyDigestReleasesAllTemporaryArrayReservations() throws { + var meter = try lifecycleMeter() + try meter.retain(41) + #expect(try SchemaExampleDigests.policy(.init(), meter: &meter).count == 64) + #expect(meter.used(.retainedBytes) == 41 + 64) + #expect(meter.used(.workUnits) > 0 && meter.used(.compiledPlanBytes) == 0) +} diff --git a/Tests/JSONSchemaExamplesTests/PreparationAndCertificationTests.swift b/Tests/JSONSchemaExamplesTests/PreparationAndCertificationTests.swift new file mode 100644 index 0000000..05c2919 --- /dev/null +++ b/Tests/JSONSchemaExamplesTests/PreparationAndCertificationTests.swift @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: Apache-2.0 +import JSON +import JSONSchema +import JSONSchemaExamples +import Testing + +private func document(_ text: String, uri: String? = nil) throws -> JSONSchemaDocument { + try JSONSchemaDocument(JSONParser().parse(text), retrievalURI: uri.map { try JSONSchemaURI($0) }) +} +private func generator(_ text: String, options: JSONSchemaExampleOptions = .init()) throws -> JSONSchemaExampleGenerator { + try JSONSchemaExampleGenerator(document: document(text), options: options) +} + +@Test func g1TrueAndEmptySchemasReturnNativeCertifiedNull() throws { + for text in ["true", "{}"] { + let prepared = try generator(text) + let result = try prepared.generate(seed: 42) + #expect(result.value.isNull) + #expect(result.validation.isValid && result.validation.diagnosticsComplete) + #expect(result.attempts == 1 && result.seed == 42) + #expect(result.statistics[.evaluationWorkUnits] > 0) + #expect(result.generatorVersion == "s3-candidates-v4") + let native = try JSONSchemaCompiler().compile(prepared.document).validate(result.value) + #expect(native.isValid == result.validation.isValid) + } +} + +@Test func g1ExactConstPreservesLargeDecimalAndScalarKeys() throws { + let prepared = try generator(#"{"const":{"é":9007199254740994.5,"e\u0301":9007199254740993}}"#) + let result = try prepared.generate() + let expected = try JSONParser().parse(#"{"é":9007199254740994.5,"e\u0301":9007199254740993}"#) + #expect(result.value == expected) + #expect(result.value.objectValue?.count == 2) + #expect(result.validation.isValid) +} + +@Test func g1NullConstIsPresentAndG3TypedStringConstructionIsNativeCertified() throws { + #expect(try generator(#"{"const":null}"#).generate().value.isNull) + let typed = try generator(#"{"type":"string"}"#).generate() + #expect(typed.value == .string("") && typed.validation.isValid) +} + +@Test func g1InvalidConstNeverEscapesRootValidation() throws { + #expect(throws: JSONSchemaExampleFailure.searchExhausted(attempts: 1)) { + try generator(#"{"const":1,"type":"string"}"#).generate() + } +} + +@Test func g1InvalidEnumAttemptRemainsInCumulativeLedger() throws { + let prepared = try generator(#"{"enum":[1,"ok"],"type":"string"}"#) + let result = try prepared.generate() + #expect(result.value == .string("ok") && result.attempts == 2) + let compiled = try JSONSchemaCompiler().compile(prepared.document) + let finalOnly = try JSONSchemaValidator().validateWithReport(.string("ok"), against: compiled) + #expect(result.statistics[.evaluationWorkUnits] > finalOnly.statistics[.evaluationWorkUnits]) + let again = try prepared.generate() + #expect(again.statistics == result.statistics && again.value == result.value) +} + +@Test func g1AnnotationsAreCandidatesAndNotRepairs() throws { + let result = try generator(#"{"type":"string","default":1,"examples":["ok"]}"#).generate() + #expect(result.value == .string("ok") && result.attempts == 2) + let authored = try generator(#"{"type":"object","required":["id"],"default":{}}"#) + let built = try authored.generate() + #expect(built.value.objectValue?["id"]?.isNull == true && built.attempts == 2) + #expect(authored.document.raw.objectValue?["default"]?.objectValue?.isEmpty == true) +} + +@Test func g1StaticAnchorAndEmbeddedResourceUseCompilerTargets() throws { + let prepared = try generator(##"{"$id":"https://e.test/root","$defs":{"x":{"$id":"nested","$defs":{"v":{"$anchor":"pick","const":9007199254740993}}}},"$ref":"nested#pick"}"##) + let result = try prepared.generate() + #expect(try result.value == JSONParser().parse("9007199254740993")) + #expect(result.validation.isValid) +} + +@Test func g1OfflineRegistryDoesNotGuessFromLastPathComponent() throws { + let a = try document(#"{"const":"first"}"#, uri: "https://a.test/schema") + let b = try document(#"{"const":"second"}"#, uri: "https://b.test/schema") + let registry = try JSONSchemaRegistry(documents: [a,b]) + let prepared = try JSONSchemaExampleGenerator(document: document(#"{"$ref":"https://b.test/schema"}"#), registry: registry) + #expect(try prepared.generate().value == .string("second")) + #expect(prepared.registry.documents.count == 2) +} + +@Test func g1ReferenceSiblingConstraintsRemainAuthoritative() throws { + #expect(throws: JSONSchemaExampleFailure.searchExhausted(attempts: 1)) { + try generator(##"{"$defs":{"x":{"const":1}},"$ref":"#/$defs/x","type":"string"}"##).generate() + } +} + +@Test func g1MissingResourceRemainsNativeCompilationFailure() throws { + do { + _ = try generator(#"{"$ref":"https://missing.test/schema"}"#) + Issue.record("Unresolved reference was accepted") + } catch let error as JSONSchemaCompilationFailure { + #expect(error.category == .missingResource) + } +} + +@Test func g1DynamicNavigationAndCyclesFailExplicitly() throws { + #expect(throws: JSONSchemaExampleFailure.unsupportedFeature(.dynamicReference)) { + try generator(##"{"$dynamicAnchor":"node","$dynamicRef":"#node"}"##).generate() + } + #expect(throws: JSONSchemaExampleFailure.unsupportedFeature(.cyclicReference)) { + try generator(##"{"$ref":"#"}"##).generate() + } +} + +@Test func g1UnsatisfiableProofsAreRestrictedToSoundRootForms() throws { + #expect(throws: JSONSchemaExampleFailure.provenUnsatisfiable(.falseSchema)) { try generator("false").generate() } + for text in [#"{"not":true}"#, #"{"not":{}}"#] { + #expect(throws: JSONSchemaExampleFailure.provenUnsatisfiable(.notTrue)) { try generator(text).generate() } + } + #expect(throws: JSONSchemaExampleFailure.unsupportedFeature(.candidateConstruction)) { + try generator(#"{"not":{"type":"string"}}"#).generate() + } +} + +@Test func g1InactiveApplicatorCannotProduceNotTrueProof() throws { + let meta = try document(#"{"$id":"https://e.test/core-only","$vocabulary":{"https://json-schema.org/draft/2020-12/vocab/core":true}}"#, uri: "https://e.test/core-only") + let prepared = try JSONSchemaExampleGenerator(document: document(#"{"$schema":"https://e.test/core-only","not":{}}"#), registry: .init(documents: [meta])) + #expect(throws: JSONSchemaExampleFailure.unsupportedFeature(.candidateConstruction)) { try prepared.generate() } + let plan = try JSONSchemaCompiler().compile(prepared.document, registry: prepared.registry) + #expect(try plan.validate(.null).isValid) +} + +@Test func g1AttemptLimitAndGeneratorConfigurationAreExact() throws { + #expect(throws: JSONSchemaExampleFailure.searchExhausted(attempts: 1)) { + try generator(#"{"enum":[1,"ok"],"type":"string"}"#, options: .init(maximumAttempts: 1)).generate() + } + for limit in [UInt64(0), UInt64.max] { + #expect(throws: JSONSchemaExampleFailure.invalidConfiguration(.attemptLimit)) { + try generator("true", options: .init(maximumAttempts: limit)) + } + } +} + +@Test func g1PoliciesRemainSeparateAndRequiredFormatAssertionWins() throws { + let options = JSONSchemaExampleOptions(compilationOptions: .init(formatAssertion: true), validationOptions: .init(output: .flag)) + #expect(throws: JSONSchemaExampleFailure.searchExhausted(attempts: 1)) { + try generator(#"{"format":"date","const":"not-a-date"}"#, options: options).generate() + } + let meta = try document(#"{"$id":"https://e.test/assertion","$schema":"https://json-schema.org/draft/2020-12/schema","$vocabulary":{"https://json-schema.org/draft/2020-12/vocab/core":true,"https://json-schema.org/draft/2020-12/vocab/meta-data":true,"https://json-schema.org/draft/2020-12/vocab/format-assertion":true}}"#, uri: "https://e.test/assertion") + let required = try JSONSchemaExampleGenerator(document: document(#"{"$schema":"https://e.test/assertion","format":"date","default":"not-a-date"}"#), registry: .init(documents: [meta]), options: .init(maximumAttempts: 1)) + let requiredPlan = try JSONSchemaCompiler().compile(required.document, registry: required.registry) + #expect(try !requiredPlan.validate(.string("not-a-date")).isValid) + #expect(!required.options.validationOptions.formatAssertion) + #expect(throws: JSONSchemaExampleFailure.searchExhausted(attempts: 1)) { try required.generate() } + let result = try generator(#"{"const":"ok","title":"label"}"#, options: .init(validationOptions: .init(output: .flag, collectAnnotations: true))).generate() + #expect(result.validation.outputStyle == .flag && result.validationOptions.collectAnnotations) + #expect(result.validation.annotations.contains { $0.keyword.string == "title" && $0.value == .string("label") }) +} + +@Test func g1OperationalFailureIsNotAnInvalidCandidateRetry() throws { + var limits = JSONLimits.balanced; limits[.evaluationWorkUnits] = 0 + do { + _ = try generator(#"{"enum":[1,2]}"#, options: .init(validationOptions: .init(limits: limits))).generate() + Issue.record("Evaluation quota was ignored") + } catch let error as JSONFailure { + #expect(error.category == .resource) + #expect(error.resource?.metric == .evaluationWorkUnits) + } + var discovery = JSONLimits.balanced; discovery[.schemaNodes] = 0 + do { + _ = try generator(#"{"const":1}"#, options: .init(limits: discovery)).generate() + Issue.record("Declaration discovery quota was ignored") + } catch let error as JSONFailure { + #expect(error.category == .resource && error.resource?.metric == .schemaNodes) + } +} + +@Test func g1SharedWorkBoundaryIncludesEarlierInvalidVerdict() throws { + let source = #"{"enum":[1,"ok"],"type":"string"}"# + let measured = try generator(source).generate() + var limits = JSONLimits.balanced; limits[.workUnits] = measured.statistics[.workUnits] + let exact = try generator(source, options: .init(limits: limits)).generate() + #expect(exact.statistics[.workUnits] == limits[.workUnits]) + limits[.workUnits] -= 1 + do { + _ = try generator(source, options: .init(limits: limits)).generate() + Issue.record("One-unit-short ledger returned a result") + } catch let error as JSONFailure { #expect(error.category == .resource && error.resource?.metric == .workUnits) } +} + +@Test func g1SourceDigestNormalizesKeysAndNumbersButRetainsRegistryOrder() throws { + let a = try generator(#"{"const":{"b":1.0,"a":2}}"#) + let b = try generator(#"{"const":{"a":2,"b":1}}"#) + #expect(a.sourceSnapshotDigest == b.sourceSnapshotDigest && a.sourceSnapshotDigest.count == 64) + #expect(a.preparationStatistics[.compileWorkUnits] > 0) + let x = try document("true", uri: "https://a.test/x"), y = try document("true", uri: "https://b.test/x") + let left = try JSONSchemaExampleGenerator(document: a.document, registry: .init(documents: [x,y])) + let right = try JSONSchemaExampleGenerator(document: a.document, registry: .init(documents: [y,x])) + #expect(left.sourceSnapshotDigest != right.sourceSnapshotDigest) + let distinct = try generator(#"{"const":{"e\u0301":2,"b":1}}"#) + #expect(a.sourceSnapshotDigest != distinct.sourceSnapshotDigest) +} + +@Test func g1QualifiedBridgeHasExactLocationsPoliciesAndCheckedEdges() throws { + let plan = try JSONSchemaCompiler(options: .init(formatAssertion: true)).compile(document(##"{"$id":"https://e.test/root","$defs":{"child":{"$id":"https://e.test/child","const":7}},"$ref":"https://e.test/child"}"##)) + var meter = try JSONWorkMeter(limits: .balanced, domain: .core) + let root = plan.exampleRoot + let ref = try root.reference(at: 0, meter: &meter) + let target = try ref.target.declaration(meter: &meter) + #expect(target.resourceURI.string == "https://e.test/child") + #expect(target.documentPointer.tokens.map(\.string) == ["$defs","child"]) + #expect(target.vocabulary.applies(.const) && target.formatAssertion) + #expect(!ref.requiresDynamicScope && ref.keyword == .ref) + var found = false + for index in 0.. 0) + #expect(throws: JSONFailure(.configuration, code: .invalidOperation)) { try root.reference(at: -1, meter: &meter) } +} + +@Test func g1CancellationTerminatesAnInvocationWithoutPoisoningPreparation() async throws { + let prepared = try generator(#"{"const":42}"#) + let task = Task { + withUnsafeCurrentTask { $0?.cancel() } + return try prepared.generate() + } + do { _ = try await task.value; Issue.record("Cancelled invocation returned a certificate") } + catch is CancellationError { } + #expect(try prepared.generate().validation.isValid) +} + +@Test func g1SharedPreparedGeneratorHasIndependentConcurrentInvocations() async throws { + let prepared = try generator(#"{"enum":[1,"ok"],"type":"string"}"#) + let expected = try prepared.generate() + try await withThrowingTaskGroup(of: JSONSchemaExampleResult.self) { group in + for seed in 0..<16 { group.addTask { try prepared.generate(seed: UInt64(seed)) } } + var count = 0 + for try await result in group { + #expect(result.value == expected.value && result.validation.isValid) + #expect(result.statistics == expected.statistics && result.attempts == 2) + count += 1 + } + #expect(count == 16) + } + let demos = try (0..<16).map { try prepared.generate(seed: UInt64($0), profile: .demonstration) } + try await withThrowingTaskGroup(of: JSONSchemaExampleResult.self) { group in + for seed in 0..<16 { group.addTask { try prepared.generate(seed: UInt64(seed), profile: .demonstration) } } + var count = 0 + for try await result in group { + let expected = demos[Int(result.seed)] + #expect(result.value == expected.value && result.traceDigest == expected.traceDigest) + #expect(result.randomDraws == expected.randomDraws && result.statistics == expected.statistics) + count += 1 + } + #expect(count == 16) + } +} diff --git a/Tests/JSONSchemaExamplesTests/QualificationMatrixTests.swift b/Tests/JSONSchemaExamplesTests/QualificationMatrixTests.swift new file mode 100644 index 0000000..a4b2831 --- /dev/null +++ b/Tests/JSONSchemaExamplesTests/QualificationMatrixTests.swift @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +import Foundation +import JSON +import JSONSchema +import JSONSchemaExamples +import Testing + +private func g7Cases() throws -> [JSON] { + let path = URL(fileURLWithPath: #filePath).deletingLastPathComponent().deletingLastPathComponent() + .appendingPathComponent("Fixtures/SchemaExamples/S3-G7-seeded-v1.json") + let handle = try FileHandle(forReadingFrom: path) + let data = try handle.readToEnd() ?? Data() + try handle.close() + let root = try JSONParser().parse(data) + return try #require(root.objectValue?["cases"]?.arrayValue) +} + +@Test func g7SeededGrammarAlwaysCertifiesRoundtripsAndRejectsKnownMutations() throws { + let cases = try g7Cases() + #expect(cases.count == 64) + var certified = 0, invalid = 0 + for row in cases { + let schema = try #require(row.objectValue?["schema"]) + let document = try JSONSchemaDocument(schema) + let generator = try JSONSchemaExampleGenerator(document: document) + let independent = try JSONSchemaCompiler().compile(document) + #expect(try !independent.validate(#require(row.objectValue?["invalid"])).isValid) + invalid += 1 + for profile in [JSONSchemaExampleProfile.minimal, .demonstration] { + for seed in [UInt64(0), 1, 42, UInt64.max] { + let result = try generator.generate(seed: seed, profile: profile) + #expect(result.validation.isValid && result.validation.diagnosticsComplete) + #expect(try independent.validate(result.value).isValid) + #expect(try JSONParser().parse(JSONWriter().write(result.value)) == result.value) + #expect(result.statistics[.evaluationWorkUnits] > 0) + #expect(result.statistics[.workUnits] <= generator.options.limits[.workUnits]) + #expect(result.attempts > 0 && result.attempts <= generator.options.maximumAttempts) + certified += 1 + } + } + } + #expect(certified == 512 && invalid == 64) + print("S3_G7_MATRIX passed schemas=64 certified=512 invalid=64") +} + +@Test func g7SchemaMemberPermutationPreservesScalarIdentityAndWitnesses() throws { + for i in 0..<8 { + let first = try JSONParser().parse("{\"type\":\"object\",\"required\":[\"é\",\"e\\u0301\"],\"properties\":{\"é\":{\"const\":\(i)},\"e\\u0301\":{\"const\":\(i+1)}}}") + let second = try JSONParser().parse(JSONWriter(options: .init(keyOrder: .scalar)).write(first)) + let a = try JSONSchemaExampleGenerator(document: .init(first)) + let b = try JSONSchemaExampleGenerator(document: .init(second)) + #expect(a.sourceSnapshotDigest == b.sourceSnapshotDigest) + let x = try a.generate(seed: 42), y = try b.generate(seed: 42) + #expect(x.value == y.value && x.traceDigest == y.traceDigest) + #expect(x.value.objectValue?.count == 2) + #expect(x.value.objectValue?["é"] != x.value.objectValue?["e\u{301}"]) + #expect(try JSONSchemaCompiler().compile(a.document).validate(y.value).isValid) + } +} diff --git a/Tests/JSONSchemaExamplesTests/TypedConstructionTests.swift b/Tests/JSONSchemaExamplesTests/TypedConstructionTests.swift new file mode 100644 index 0000000..a43c480 --- /dev/null +++ b/Tests/JSONSchemaExamplesTests/TypedConstructionTests.swift @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: Apache-2.0 +import Foundation +import JSON +import JSONSchema +@testable import JSONSchemaExamples +import Testing + +private func make(_ text: String, options: JSONSchemaExampleOptions = .init()) throws -> JSONSchemaExampleGenerator { + try .init(document: .init(JSONParser().parse(text)), options: options) +} +private let fixtureDirectory = URL(fileURLWithPath: #filePath).deletingLastPathComponent().deletingLastPathComponent().appendingPathComponent("Fixtures/SchemaExamples") +private func fixture(_ name: String) throws -> JSON { try JSONParser().parse(Data(contentsOf: fixtureDirectory.appendingPathComponent(name))) } +private func requireResource(_ error: any Error, _ metric: JSONResourceMetric) { + guard let failure = error as? JSONFailure else { Issue.record("Expected a resource error"); return } + #expect(failure.category == .resource && failure.resource?.metric == metric) +} + +@Test func g3All24RequiredFixturesAreNativeCertifiedForBothProfilesAndFourSeeds() throws { + let data = try fixture("S3-integral-correction-v1.json"), manifest = try fixture("Manifest.json") + let ids = try #require(manifest.objectValue?["g3_must_generate_ids"]?.arrayValue) + let cases = try #require(data.objectValue?["cases"]?.arrayValue) + var observed: [String] = [], generated = 0, witnesses = 0 + for value in cases { + let c = try #require(value.objectValue) + guard c["acceptanceGate"] == .string("G3"), c["generatorExpectation"] == .string("must-generate") else { continue } + let id = try #require(c["id"]?.stringValue), schema = try #require(c["schema"]) + let assertion = c["validation"]?.objectValue?["formatAssertion"]?.booleanValue == true + let options = JSONSchemaExampleOptions(validationOptions: .init(formatAssertion: assertion)) + let generator = try JSONSchemaExampleGenerator(document: .init(schema), options: options) + let independent = try JSONSchemaCompiler(options: options.compilationOptions).compile(generator.document, registry: generator.registry) + let validator = JSONSchemaValidator(options: options.validationOptions) + for witness in try #require(c["witnesses"]?.arrayValue) { + let instance = try #require(witness.objectValue?["instance"]) + let expected = try #require(witness.objectValue?["valid"]?.booleanValue as Bool?) + #expect(try validator.validate(instance, against: independent).isValid == expected, "\(id) witness") + witnesses += 1 + } + for profile in [JSONSchemaExampleProfile.minimal, .demonstration] { + for seed in [UInt64(0), 1, 42, UInt64.max] { + let result = try generator.generate(seed: seed, profile: profile) + #expect(result.validation.isValid && result.validation.diagnosticsComplete, "\(id)") + #expect(try validator.validate(result.value, against: independent).isValid, "\(id)") + #expect(try JSONParser().parse(JSONWriter().write(result.value)) == result.value, "\(id)") + #expect(result.statistics[.workUnits] >= result.statistics[.evaluationWorkUnits]) + #expect(result.statistics[.workUnits] <= options.limits[.workUnits]) + generated += 1 + } + } + observed.append(id) + print("G3_FIXTURE passed id=\(id) profiles=2 seeds=4 generated=8") + } + #expect(observed.map(JSON.string) == ids && observed.count == 24 && generated == 192) + #expect(witnesses > 0) + print("G3_ACCEPTANCE passed cases=\(observed.count) generated=\(generated) witnesses=\(witnesses)") +} + +@Test func g3TwoFixtureProofsRemainSeparateFromSearchFailure() throws { + let cases = try #require(fixture("S3-integral-correction-v1.json").objectValue?["cases"]?.arrayValue) + var count = 0 + for c in cases where c.objectValue?["acceptanceGate"] == .string("G3") && c.objectValue?["generatorExpectation"] == .string("must-prove-unsatisfiable") { + let generator = try JSONSchemaExampleGenerator(document: .init(try #require(c.objectValue?["schema"]))) + let proof: JSONSchemaUnsatisfiableProof = c.objectValue?["id"] == .string("boolean-false") ? .falseSchema : .notTrue + #expect(throws: JSONSchemaExampleFailure.provenUnsatisfiable(proof)) { try generator.generate() } + count += 1 + } + #expect(count == 2) +} + +@Test func g3NestedScalarKeysAndConjunctivePropertiesNeverOverwrite() throws { + let source = #"{"type":"object","allOf":[{"properties":{"é":{"type":"integer","minimum":2},"e\u0301":{"const":9}},"required":["é","e\u0301"]},{"properties":{"é":{"maximum":3}},"required":["é"]}]}"# + let result = try make(source).generate() + #expect(result.value.objectValue?.count == 2) + #expect(result.value.objectValue?["é"] == 2 && result.value.objectValue?["e\u{301}"] == 9) + #expect(result.validation.isValid) + let incompatible = #"{"type":"object","allOf":[{"properties":{"a":{"type":"integer"}},"required":["a"]},{"properties":{"a":{"type":"string"}},"required":["a"]}]}"# + #expect(throws: JSONSchemaExampleFailure.unsupportedFeature(.candidateConstruction)) { try make(incompatible).generate() } +} + +@Test func g3EachConjunctControlsAdditionalPropertiesIndependently() throws { + let source = #"{"type":"object","properties":{"a":{"const":1}},"additionalProperties":false,"allOf":[{"required":["b"],"properties":{"b":{"const":2}}}]}"# + #expect(throws: JSONSchemaExampleFailure.unsupportedFeature(.candidateConstruction)) { try make(source).generate() } +} + +@Test func g3NegativeAndOpenExactNumericIntervalsNeverUseEpsilon() throws { + for (schema, expected) in [ + (#"{"type":"integer","maximum":-1.25}"#, "-2"), + (#"{"type":"integer","exclusiveMinimum":-2,"maximum":-1}"#, "-1"), + (#"{"type":"number","exclusiveMinimum":0.000000000000000000000001,"exclusiveMaximum":0.000000000000000000000002}"#, "11e-25"), + (#"{"type":"number","minimum":-0.049,"maximum":-0.021,"multipleOf":0.01}"#, "-0.04"), + (#"{"type":"integer","minimum":1,"multipleOf":2.5}"#, "5"), + (#"{"type":"integer","minimum":123456789012345678901234567890123456789}"#, "123456789012345678901234567890123456789") + ] { + let result = try make(schema).generate() + #expect(try result.value == JSONParser().parse(expected)) + #expect(result.validation.isValid && result.statistics[.numericWorkUnits] > 0) + } +} + +@Test func g3ScalarLengthUsesDecodedScalarsAndPreservesCorpusSpelling() throws { + let options = JSONSchemaExampleOptions(corpus: .init(values: [.string("e\u{301}")])) + let result = try make(#"{"type":"string","minLength":2,"maxLength":2}"#, options: options).generate(profile: .demonstration) + #expect(result.value == .string("e\u{301}") && result.validation.isValid) +} + +@Test func g3AssertionPolicyRemainsNativeAndContentMetadataDoesNotExecute() throws { + let source = #"{"type":"string","format":"email","contentEncoding":"base64","contentMediaType":"text/html"}"# + #expect(try make(source).generate().value == .string("")) + let asserted = try make(source, options: .init(validationOptions: .init(formatAssertion: true))).generate() + #expect(asserted.value == .string("example@example.com") && asserted.validation.isValid) +} + +@Test func g3MinimumPropertiesAndUniqueArrayElementsHaveCheckedConstruction() throws { + let object = try make(#"{"type":"object","minProperties":3,"maxProperties":3,"additionalProperties":{"type":"integer"}}"#).generate() + #expect(object.value.objectValue?.members.map(\.key.string) == ["p0","p1","p2"]) + let array = try make(#"{"type":"array","minItems":3,"uniqueItems":true,"items":{"type":"integer"}}"#).generate() + #expect(array.value == .array([0,1,2]) && array.validation.isValid) +} + +@Test func g3ConstructedArraysRetainNestedElementsAndDepth() throws { + let source = #"{"type":"array","minItems":2,"items":{"type":"array","minItems":2,"items":{"type":"integer"}}}"# + let result = try make(source).generate() + #expect(result.value == .array([.array([0,0]), .array([0,0])])) +} + +@Test func g3TypedConstructionHasAnExactCumulativeWorkBoundary() throws { + let source = #"{"type":"object","required":["v"],"properties":{"v":{"type":"number","minimum":0.02,"multipleOf":0.01}},"default":{}}"# + let result = try make(source).generate() + #expect(result.attempts == 2 && result.validation.isValid) + var limits = JSONLimits.balanced; limits[.workUnits] = result.statistics[.workUnits] + #expect(try make(source, options: .init(limits: limits)).generate().statistics[.workUnits] == limits[.workUnits]) + limits[.workUnits] -= 1 + do { _ = try make(source, options: .init(limits: limits)).generate(); Issue.record("Cumulative budget reset") } + catch { requireResource(error, .workUnits) } +} + +@Test func g3TypedStagingChecksDepthCardinalityAndNumericWorkBeforeGrowth() throws { + for (schema, metric, cap) in [ + (#"{"type":"array","minItems":3,"items":{"type":"integer"}}"#, JSONResourceMetric.arrayItems, UInt64(2)), + (#"{"type":"object","minProperties":2}"#, .objectMembers, 1), + (#"{"type":"array","minItems":1,"items":{"type":"array"}}"#, .maxDepth, 1), + (#"{"type":"number","minimum":0.02,"multipleOf":0.01}"#, .numericWorkUnits, 0), + (#"{"type":"integer","minimum":9007199254740993}"#, .numberDigits, 3) + ] { + var limits = JSONLimits.balanced; limits[metric] = cap + do { _ = try make(schema, options: .init(limits: limits)).generate(); Issue.record("Staging cap ignored") } + catch { requireResource(error, metric) } + } +} + +@Test func g3DenseAlignmentFailsFinitelyWithoutApproximation() throws { + do { _ = try make(#"{"type":"number","exclusiveMinimum":1e10000,"multipleOf":0.01}"#).generate(); Issue.record("Unbounded dense decimal") } + catch { requireResource(error, .numberDigits) } +} + +@Test func g3OperationalValidationFailureNeverAdvancesTypedSearch() throws { + var limits = JSONLimits.balanced; limits[.evaluationWorkUnits] = 0 + do { _ = try make(#"{"type":"integer"}"#, options: .init(validationOptions: .init(limits: limits))).generate(); Issue.record("Operational error retried") } + catch { requireResource(error, .evaluationWorkUnits) } +} + +@Test func g3CancellationInsideTypedFramesIsTerminal() async throws { + let generator = try make(#"{"type":"object","required":["a"],"properties":{"a":{"type":"integer"}}}"#) + let plan = try JSONSchemaCompiler().compile(generator.document) + let task = Task { + var limits = JSONLimits.balanced; limits[.cancellationQuantum] = 1 + var meter = try JSONWorkMeter(limits: limits, domain: .core) + var random = SchemaExampleRandom(seed: 0) + var trace = try SchemaExampleTrace(snapshot: "s", corpus: "c", policy: "p", seed: 0, profile: .minimal, meter: &meter) + let trigger = meter.used(.workUnits) + 100 + meter.checkpointObserver = { work in if work >= trigger { withUnsafeCurrentTask { $0?.cancel() } } } + #expect(throws: CancellationError.self) { + try SchemaExampleConstructor.construct(nodes: [plan.exampleRoot], variation: 0, profile: .minimal, corpus: [], assertion: false, + random: &random, trace: &trace, meter: &meter) + } + #expect(throws: CancellationError.self) { try meter.work() } + } + try await task.value + #expect(try generator.generate().validation.isValid) +} + +@Test func g3OneHundredRepeatedTypedInvocationsHaveNoStateDrift() throws { + let generator = try make(#"{"type":"object","required":["id","label"],"properties":{"id":{"type":"integer","minimum":9007199254740993},"label":{"type":"string","minLength":2}}}"#) + let expected = try generator.generate(seed: 42, profile: .demonstration) + for _ in 0..<100 { + let result = try generator.generate(seed: 42, profile: .demonstration) + #expect(result.value == expected.value && result.traceDigest == expected.traceDigest && result.statistics == expected.statistics) + } +} + +@Test func g3SharedTypedGeneratorHasIndependentConcurrentInvocations() async throws { + let generator = try make(#"{"type":"array","minItems":3,"uniqueItems":true,"items":{"type":"integer","minimum":9007199254740993}}"#) + let minimal = try (0..<16).map { try generator.generate(seed: UInt64($0)) } + let demonstration = try (0..<16).map { try generator.generate(seed: UInt64($0), profile: .demonstration) } + try await withThrowingTaskGroup(of: JSONSchemaExampleResult.self) { group in + for seed in 0..<16 { + group.addTask { try generator.generate(seed: UInt64(seed)) } + group.addTask { try generator.generate(seed: UInt64(seed), profile: .demonstration) } + } + var count = 0 + for try await result in group { + let expected = result.profile == .minimal ? minimal[Int(result.seed)] : demonstration[Int(result.seed)] + #expect(result.value == expected.value && result.traceDigest == expected.traceDigest && result.statistics == expected.statistics) + count += 1 + } + #expect(count == 32) + } +} + +@Test func g3PrefixIndicesAboveNineRemainPositionalDecimalText() throws { + var members: [JSON] = [] + for index in 0..<12 { members.append(.object(try JSONObject(members: [.init("const", value: .number(JSONNumber(integer: index)))]))) } + let schema = try JSONObject(members: [.init("type", value: .string("array")), .init("minItems", value: 12), .init("prefixItems", value: .array(members)), .init("items", value: false)]) + let result = try JSONSchemaExampleGenerator(document: .init(.object(schema))).generate() + #expect(result.value == .array((0..<12).map { .number(JSONNumber(integer: $0)) })) +} + +@Test func g3SignedRoundingMatchesIndependentIntegerArithmeticGrid() throws { + var meter = try JSONWorkMeter(limits: .offline, domain: .core) + var observed = 0 + for numerator in -17...17 { + for divisor in 1...7 { + for up in [false, true] { + for open in [false, true] { + for variation in [UInt64(0),1,2] { + var quotient = numerator / divisor + let remainder = numerator % divisor + if up && remainder > 0 { quotient += 1 } + if !up && remainder < 0 { quotient -= 1 } + if open && remainder == 0 { quotient += up ? 1 : -1 } + quotient += (up ? 1 : -1) * Int(variation) + let result = try SchemaExampleDecimal.rounded(JSONNumber(integer: numerator), step: JSONNumber(integer: divisor), + up: up, open: open, variation: variation, meter: &meter) + #expect(result == JSONNumber(integer: quotient * divisor)) + observed += 1 + } + } + } + } + } + #expect(observed == 2940 && meter.used(.numericWorkUnits) > 0) +} + +@Test func g3RepeatedStaticTargetsAreDAGSharingAndRecursiveBaseCasesRemainFinite() throws { + let dag = ##"{"$defs":{"x":{"type":"integer"}},"allOf":[{"$ref":"#/$defs/x"},{"$ref":"#/$defs/x"}]}"## + #expect(try make(dag).generate().value == 0) + let recursive = ##"{"$defs":{"x":{"type":"array","items":{"$ref":"#/$defs/x"}}},"$ref":"#/$defs/x"}"## + #expect(try make(recursive).generate().value == .array([])) +} diff --git a/Tests/SchemaExamplesCompilerFixtures/ArrayFrameSendable.swift b/Tests/SchemaExamplesCompilerFixtures/ArrayFrameSendable.swift new file mode 100644 index 0000000..058e8df --- /dev/null +++ b/Tests/SchemaExamplesCompilerFixtures/ArrayFrameSendable.swift @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 +@testable import JSONSchemaExamples +func send(_ value: borrowing T) {} +func transfer(_ state: borrowing SchemaExampleArrayFrame) { send(state) } diff --git a/Tests/SchemaExamplesCompilerFixtures/FrameSendable.swift b/Tests/SchemaExamplesCompilerFixtures/FrameSendable.swift new file mode 100644 index 0000000..01992d0 --- /dev/null +++ b/Tests/SchemaExamplesCompilerFixtures/FrameSendable.swift @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 +@testable import JSONSchemaExamples +func send(_ value: borrowing T) {} +func transfer(_ state: borrowing SchemaExampleFrame) { send(state) } diff --git a/Tests/SchemaExamplesCompilerFixtures/MoveAndShare.swift b/Tests/SchemaExamplesCompilerFixtures/MoveAndShare.swift new file mode 100644 index 0000000..6188957 --- /dev/null +++ b/Tests/SchemaExamplesCompilerFixtures/MoveAndShare.swift @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +@testable import JSONSchemaExamples +func move(_ state: consuming SchemaExampleRandom) { let moved = state; _ = consume moved } +func share(_ value: T) {} +func sharePrepared(_ value: JSONSchemaExampleGenerator) { share(value) } diff --git a/Tests/SchemaExamplesCompilerFixtures/ObjectFrameSendable.swift b/Tests/SchemaExamplesCompilerFixtures/ObjectFrameSendable.swift new file mode 100644 index 0000000..c5ff5e0 --- /dev/null +++ b/Tests/SchemaExamplesCompilerFixtures/ObjectFrameSendable.swift @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 +@testable import JSONSchemaExamples +func send(_ value: borrowing T) {} +func transfer(_ state: borrowing SchemaExampleObjectFrame) { send(state) } diff --git a/Tests/SchemaExamplesCompilerFixtures/RandomCopy.swift b/Tests/SchemaExamplesCompilerFixtures/RandomCopy.swift new file mode 100644 index 0000000..959f863 --- /dev/null +++ b/Tests/SchemaExamplesCompilerFixtures/RandomCopy.swift @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: Apache-2.0 +@testable import JSONSchemaExamples +func duplicate(_ state: consuming SchemaExampleRandom) { let other = state; _ = consume state; _ = consume other } diff --git a/Tests/SchemaExamplesCompilerFixtures/RandomSendable.swift b/Tests/SchemaExamplesCompilerFixtures/RandomSendable.swift new file mode 100644 index 0000000..b83d143 --- /dev/null +++ b/Tests/SchemaExamplesCompilerFixtures/RandomSendable.swift @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 +@testable import JSONSchemaExamples +func send(_ value: borrowing T) {} +func transfer(_ state: borrowing SchemaExampleRandom) { send(state) } diff --git a/Tests/SchemaExamplesCompilerFixtures/TraceCopy.swift b/Tests/SchemaExamplesCompilerFixtures/TraceCopy.swift new file mode 100644 index 0000000..06fa370 --- /dev/null +++ b/Tests/SchemaExamplesCompilerFixtures/TraceCopy.swift @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: Apache-2.0 +@testable import JSONSchemaExamples +func duplicate(_ state: consuming SchemaExampleTrace) { let other = state; _ = consume state; _ = consume other } diff --git a/Tests/SchemaExamplesCompilerFixtures/TraceSendable.swift b/Tests/SchemaExamplesCompilerFixtures/TraceSendable.swift new file mode 100644 index 0000000..6d7a4aa --- /dev/null +++ b/Tests/SchemaExamplesCompilerFixtures/TraceSendable.swift @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 +@testable import JSONSchemaExamples +func send(_ value: borrowing T) {} +func transfer(_ state: borrowing SchemaExampleTrace) { send(state) } diff --git a/Tests/SchemaExamplesCompilerFixtures/TransportSendable.swift b/Tests/SchemaExamplesCompilerFixtures/TransportSendable.swift new file mode 100644 index 0000000..b30b9db --- /dev/null +++ b/Tests/SchemaExamplesCompilerFixtures/TransportSendable.swift @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 +@testable import JSONSchemaExamples +func send(_ value: borrowing T) {} +func transfer(_ state: borrowing SchemaExampleTransport) { send(state) } diff --git a/Tools/SnapshotQualification/Package.swift b/Tools/SnapshotQualification/Package.swift new file mode 100644 index 0000000..0fec6e8 --- /dev/null +++ b/Tools/SnapshotQualification/Package.swift @@ -0,0 +1,12 @@ +// swift-tools-version: 6.4 +// SPDX-License-Identifier: Apache-2.0 +import PackageDescription +let package = Package( + name: "SnapshotQualification", + platforms: [.macOS("27.0"), .iOS("27.0")], + dependencies: [.package(name: "Swift-JSON", path: "../..")], + targets: [.executableTarget(name: "SnapshotConsumer", dependencies: [ + .product(name: "JSON", package: "Swift-JSON"), + .product(name: "JSONNavigation", package: "Swift-JSON"), + .product(name: "JSONInspection", package: "Swift-JSON")])], + swiftLanguageModes: [.v6]) diff --git a/Tools/SnapshotQualification/README.md b/Tools/SnapshotQualification/README.md new file mode 100644 index 0000000..818e0df --- /dev/null +++ b/Tools/SnapshotQualification/README.md @@ -0,0 +1,10 @@ +# Public snapshot consumer + +This independent SwiftPM package imports only public JSON, JSONNavigation and +JSONInspection APIs. Run `swift run -c release --package-path +Tools/SnapshotQualification SnapshotConsumer` on a qualified Swift 6.4 platform. +Its fourteen named checks exercise exact source-bound metadata and separate +bounded lookups, including negative capacity, bounds and quota cases. It does +not establish a release or physical-device qualification. The associated +`Scripts/qualify_snapshot.py` executes all three DocC examples and full-codegen +external-access rejections, with source-bound receipts. diff --git a/Tools/SnapshotQualification/Sources/SnapshotConsumer/Main.swift b/Tools/SnapshotQualification/Sources/SnapshotConsumer/Main.swift new file mode 100644 index 0000000..13b0590 --- /dev/null +++ b/Tools/SnapshotQualification/Sources/SnapshotConsumer/Main.swift @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: Apache-2.0 +import Foundation +import JSON +import JSONNavigation +import JSONInspection + +private func expect(_ condition: @autoclosure () throws -> Bool) throws { + let satisfied = try condition(); precondition(satisfied) +} +private func check(_ name: String, _ body: () throws -> Void) rethrows { + try body(); print("SNAPSHOT_CHECK \(name) passed") +} +private func failure(_ body: () throws -> Void) -> JSONFailure { + do { try body(); preconditionFailure("Expected failure") } + catch let error as JSONFailure { return error } + catch { preconditionFailure("Unexpected error: \(error)") } +} +private func sendable(_ value: T) { _ = value } + +@main struct SnapshotConsumer { + static func main() async throws { + let source = #"{"":null,"0":[9007199254740993,0.1,0.0100],"é":true,"e\u0301":false,"/":{"~":[]}}"# + let document = try JSONParser().parseDocument(Data(source.utf8)) + let builder = JSONStructuralSnapshotBuilder(document: document) + let report = try builder.build() + let snapshot = report.result + sendable(builder); sendable(snapshot); sendable(try snapshot.record(at: 0)) + try check("roots") { + for text in ["null", "false", "1", #""s""#, "{}", "[]"] { + let one = try JSONStructuralSnapshotBuilder(document: .init(root: JSON.parse(text))).build().result + try expect(one.recordCount == 1) + let root = try one.record(at: 0) + try expect(root.ordinal == 0 && root.parentOrdinal == nil && root.componentFromParent == nil) + try expect(root.childCount == 0 && root.depth == 0 && root.subtreeEndExclusive == 1) + } + } + try check("typed-paths") { + try expect(try snapshot.location(for: 0).result == []) + try expect(try snapshot.location(for: 1).result == [.key("")]) + try expect(try snapshot.location(for: 2).result == [.key("0")]) + try expect(try snapshot.location(for: 3).result == [.key("0"), .index(0)]) + try expect(try snapshot.record(at: 2).componentFromParent != .index(0)) + } + try check("unicode") { + try expect(try snapshot.record(at: 6).componentFromParent == .key("é")) + try expect(try snapshot.record(at: 7).componentFromParent == .key("e\u{301}")) + try expect(try snapshot.record(at: 6).componentFromParent != snapshot.record(at: 7).componentFromParent) + } + try check("numbers") { + try expect(try snapshot.value(for: 3).result.numberValue?.integer(as: UInt64.self) == 9_007_199_254_740_993) + try expect(try snapshot.value(for: 4).result.numberValue == JSONNumber(parsing: "0.1")) + try expect(try snapshot.value(for: 5).result.numberValue?.originalLexeme == "0.0100") + try expect(try snapshot.value(for: 1).result == .null) + } + try check("duplicate-refusal") { + let error = failure { _ = try JSONParser().parse(#"{"a":0,"\u0061":1}"#) } + try expect(error.code == .duplicateKey) + } + try check("preorder-ranges") { + let expected = [10,2,6,4,5,6,7,8,10,10] + try expect(snapshot.recordCount == expected.count) + for ordinal in 0.. 0) + } + try expect(report.statistics[.nodes] == UInt64(snapshot.recordCount)) + var noNodes = JSONLimits.balanced; noNodes[.nodes] = 0 + try expect(failure { _ = try snapshot.value(for: 3, limits: noNodes) }.resource?.metric == .nodes) + print("SNAPSHOT_RESOURCE work=\(report.statistics[.workUnits]) retained=\(report.statistics[.retainedBytes]) peak=\(report.statistics.peakLogicalRetainedBytes) nodes=\(report.statistics[.nodes])") + } + try check("capacity-configuration") { + try expect(try builder.build(maximumRecords: snapshot.recordCount).result.recordCount == snapshot.recordCount) + for cap in [0, snapshot.recordCount-1] { + do { _ = try builder.build(maximumRecords: cap); preconditionFailure("No partial success") } + catch let error as JSONStructuralSnapshotFailure { try expect(error == .recordLimitExceeded(limit: cap)) } + } + var limits = JSONLimits.balanced; limits[.nodes] = 0 + try expect(failure { _ = try JSONStructuralSnapshotBuilder(document: document, limits: limits).build(maximumRecords: -1) }.category == .configuration) + } + try check("inclusive-budgets") { + var limits = JSONLimits.balanced; limits[.workUnits] = report.statistics[.workUnits] + try expect(try JSONStructuralSnapshotBuilder(document: document, limits: limits).build().result.recordCount == snapshot.recordCount) + limits[.workUnits] -= 1 + try expect(failure { _ = try JSONStructuralSnapshotBuilder(document: document, limits: limits).build() }.resource?.metric == .workUnits) + limits = .balanced; limits[.retainedBytes] = report.statistics.peakLogicalRetainedBytes + _ = try JSONStructuralSnapshotBuilder(document: document, limits: limits).build() + limits[.retainedBytes] -= 1 + try expect(failure { _ = try JSONStructuralSnapshotBuilder(document: document, limits: limits).build() }.resource?.metric == .retainedBytes) + } + try check("coverage-separation") { + let rows = try JSON.parse(#"[{"a":null},{"b":1},false]"#) + let evidence = try JSONShapeInspector(document: .init(root: rows)).inspectArray(sampling: .prefix(maximumRows: 2)).result + try expect(evidence.rowCoverage == .prefix(examined: 2, total: 3)) + try expect(evidence.observedAllObjects == true) + try expect(try JSONStructuralSnapshotBuilder(document: .init(root: rows)).build().result.recordCount == 6) + } + try await withThrowingTaskGroup(of: UInt64.self) { group in + for _ in 0..<4 { group.addTask { try snapshot.value(for: 3).statistics[.workUnits] } } + let expected = try snapshot.value(for: 3).statistics[.workUnits] + for try await work in group { try expect(work == expected) } + } + print("SNAPSHOT_CHECK concurrent-lookups passed") + print("SNAPSHOT_CONSUMER passed cases=14") + } +}