Skip to content
Closed
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## 1.1.0 — unreleased candidate

- Scoped typed reads with cumulative reports, native model projection and guarded
cursor invalidation; explicit numeric fidelity and absent/null presence.
- Native JSON value-coding conveniences and immutable exact Version 1 payload.
- Metered adaptive exact-key metadata and incremental duplicate recognition;
ordered members and exact collision confirmation remain authoritative.
- macOS/iOS 27 deployment minima and corrected schema bundle layout; public
SDK/simulator and disk-backed SwiftData consumers. Full physical-device
qualification remains a release gate. No tag/publication follows this change.
- Correct native ISO-8601 calendar fields and strict Base64 unused padding bits.
See API migration guidance; no limits/profile weights or dependencies raised.


## 1.0.0 — 2026-10-11

First stable release.
Expand Down
22 changes: 22 additions & 0 deletions Documentation/Architecture/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,25 @@ subsystem ceilings independently. WorkUnits includes subsystem work, so subtract
once. Failure/cancellation terminates the composition because no successful report
accounts for failed work. The [actual consumer interface](../ConsumerInterface.md)
and runnable consumer demonstrate this contract.

## Additive 1.1 candidate

JSON and JSONDocument provide `read(limits:_:)` and `readWithReport(limits:_:)`
with synchronous non-Sendable JSONReadCursor. Throwing String/JSONKey/Int brackets,
required kind/integer access, explicit Double/Decimal policies, raw/scalar/model
presence and native projection share one active meter. Paths are typed and bounded;
JSONAccessFailure carries the complete path and structured cause. Scope exit
invalidates handles and releases root/source ownership. Resource/cancellation
failures remain terminal when caught by a caller. See [reading](Release1.1/Reading.md).

JSON `init(encoding:coding:)`, `decode(_:coding:)` and `decodeWithReport(_:coding:)`
delegate to existing native facades. JSONCodablePayload provides
`init(preserving:limits:)`, `preservingWithReport(_:limits:)`, `value(limits:)`
and `valueWithReport(limits:)`. Its explicit Codable Version 1 stores validated
UTF-8 only and shares native parent budgets. See [persistence](Release1.1/Persistence.md).

The native ISO-8601 Date strategy now includes calendar fields as well as fractional
UTC time. Prior time-only stored values cannot recover their original calendar
date; application migration needs an independent source of that information.
Noncanonical Base64 with nonzero unused padding bits now rejects. These corrections
restore the documented strict/native contracts; they do not add a permissive mode.
19 changes: 19 additions & 0 deletions Documentation/Architecture/Release1.1/Decisions.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
# Current 1.1 decisions

The sections below preserve the initial R0/first-filter stage history. Current
successors are [Index](Index.md), [IndexMeasurements](IndexMeasurements.md),
[Reading](Reading.md), [Persistence](Persistence.md) and [Mobile](Mobile.md).
Production selects the controlled adaptive hash index with exact confirmation,
32-member threshold and first-eight scalar lookup path. Every published object
owns valid immutable metadata; construction/native keyed storage maintain it.
The guarded reader fallback, one cumulative meter and fixed Version 1 payload
are implemented. Resource packaging is corrected under the default build engine.

SIMD and independent-work parallel batches remain deferred conditional experiments.
The scalar path and checked Sendable immutable sharing remain. No measured mobile
crossover/energy evidence supports shipping a new accelerator, and the selected
wide-object change supplies the demonstrated workload benefit. This is a scope
decision, not completed SIMD/parallel qualification. E11/E12 remain deferred.
Physical iPad evidence blocks full R5/R8 readiness; simulator availability is not
physical qualification. The three protected Native check names remain unchanged.

# R0 architecture decisions

## Reader ownership and declarations
Expand Down
8 changes: 8 additions & 0 deletions Documentation/Architecture/Release1.1/Mobile.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,11 @@ iPhoneOS and iPhoneSimulator. Required subsequent evidence includes iPhone/iPad
simulator behavior/cancellation, physical-device smoke and measurements, and real
disk-backed SwiftData teardown/reopen using the exact payload. Keep every not-run
row explicit; this document does not certify those later runs.

The public runtime harness now executes exact numeric/scalar conversion, offline
plans/native regex, integrity-checked packaged schemas, Patch/Merge Patch, payload
fixtures, stream completion/terminal pulls, awaited sinks and cancellation. Mac
Swift Testing and isolated iPhone/iPad iOS 27 simulators execute three-process
disk save/reopen/update checks. The physical iPhone is connected with iOS 27.2
and developer mode enabled; signing/runtime/performance are separately qualified.
The physical iPad is unavailable and remains a mandatory R5 release blocker.
44 changes: 44 additions & 0 deletions Documentation/Architecture/Release1.1/Persistence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Exact Version 1 persistence

The implemented public wrapper is immutable `JSONCodablePayload: Sendable, Codable`.
`init(preserving:limits:)`, `preservingWithReport(_:limits:)`, `value(limits:)`
and `valueWithReport(limits:)` use the existing owned codec. Private storage is
validated UTF-8 Data only, without a DOM cache, public byte mutation or equality.

Version 1 requires format `swift-json-value`, integer version 1 and strict standard
Base64 `utf8Base64`. Compact insertion-order writing preserves admitted number
lexemes, with normalized fallback. Scalar sequences, exact decimals and array/member
order survive fixed-primitive foreign transport. Whitespace/escape/source-document
fidelity is outside this contract. Eight committed golden fixtures pin the envelope
and inner hashes; empty arrays/objects are additional behavior cases.

Construction preflights prospective Base64, fixed envelope output and text sizes.
Explicit limits intersect balanced, never widen it. Decode admits text before
decoding, checks decoded size, certifies complete inner grammar/EOF and releases the
temporary DOM. Opening is a fresh bounded parse. Copies, simultaneous byte/text/DOM
ownership, work and cancellation share the enclosing native meter when nested.
Foreign callbacks/allocations remain caller work. No meter is borrowed across an
Encoder/Decoder callback. Logical retention is not an allocator or RSS promise.

Required field types use ordinary Codable errors. Unknown marker/version and
malformed inner syntax/Base64 map to bounded corruption with a static description;
resource/configuration failures and cancellation retain their types. A caller that
catches an owned operational failure cannot certify successful native completion.
The shared Base64 decoder now rejects nonzero unused padding bits, closing a real
strict-codec gap. No legacy heuristic or corrupt-data-to-null fallback is added.

`Tools/MobileQualification` imports the four public products and SwiftData outside
the core package. Three Swift Testing cases qualify public behavior, stream/sink
completion/cancellation and disk persistence. The disk case saves eight exact
fixtures, executes each save/reopen/update phase in a separate process, reopens at the
same URL, checks scalar bytes/order/lexemes, queries/sorts ordinary fields, updates,
releases again and verifies a third reopen. Optional nil and present JSON null are
distinct. SwiftData owns its database representation; embedded JSON predicates and
cloud synchronization are not claimed. Mac, simulator and physical-device outcomes
are separate receipts, and missing device evidence blocks the full R5 gate.

The first simulator release run failed the additional weak-container assertion:
SwiftData retained a container beyond lexical scope. The debug Mac run passed it.
Retain both outcomes; do not claim that SDK lifetime is controlled by this package.
The final disk harness uses three independently exited processes on Mac/simulators,
so reopening cannot read prior model/context/container memory or SDK caches.
8 changes: 8 additions & 0 deletions Documentation/Qualification/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ and seven additional nonparameterized cases. The first production lookup increme
the independent runner adds nine budget/report vectors. There are three separate
JSON/JSONSchema consumer tests. All discoveries must be nonzero.

The current integrated 1.1 candidate checks exactly 156 root functions:
JSON 86, JSONStreaming 11, JSONSchema 38, JSONRegexRuntime 12 and JSONPatch 9.
All 173 parameterized invocations remain. The earlier counts above describe their
historical stages, not this source. Three additional public/mobile Swift Testing
functions exercise eight pinned payload fixtures and three independently exited
SwiftData disk phases. The seven-function R0 subset, all 49 cap probes, rooted
20-case consumer and independent policies retain their exact checks.

| Independent input | Observed policy |
| --- | --- |
| JSONTestSuite, pinned in FixtureRegistry.json | 318 discovered: 101 accepted, 186 rejected, 31 strict/finite-policy rejections |
Expand Down
16 changes: 16 additions & 0 deletions Examples/Consumer/Sources/Consumer/READMEExamples.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// Compiled README examples; checked by the documentation lane.
import JSON
import Foundation
import JSONSchema
import JSONPatch
import JSONStreaming
Expand Down Expand Up @@ -55,6 +56,21 @@ func runREADMEExamples() throws {
let decoded = try JSONParser().decode(Reading.self, from: bytes)
precondition(decoded == reading)

let scoped = try value.readWithReport { root in
let exactCount = try root["count"].requiredInteger(as: UInt64.self)
let optionalNote = try root.stringPresence(of: "note")
return (exactCount, optionalNote)
}
precondition(scoped.result.0 == 9_007_199_254_740_993)
precondition(scoped.result.1 == .null)
precondition(scoped.statistics[.workUnits] > 0)

let payload = try JSONCodablePayload(preserving: value)
let transported = try PropertyListEncoder().encode(payload)
let restored = try PropertyListDecoder().decode(JSONCodablePayload.self, from: transported)
let restoredValue = try restored.value()
precondition(restoredValue == value)

let schema = try JSONSchemaDocument(parser.parse(
#"{"type":"object","properties":{"count":{"type":"integer"}},"required":["count"]}"#
))
Expand Down
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,48 @@ by default; explicit ISO-8601 uses bounded UTC text with fractional seconds and
four-digit years. Foreign Codable encoders/decoders determine their own numeric and
key fidelity. Caller callbacks remain caller-owned work.

## Development toward 1.1

The 1.1 feature branches add scoped reads, native value-coding conveniences,
exact Version 1 persistence and a measured private wide-object index. The released
1.0 package remains available through the installation declaration below.
Physical iPad qualification is pending; SDK/simulator evidence is separate from
release support. See the [release plan](Documentation/Architecture/Release1.1/Plan.md).

```swift
let scoped = try value.readWithReport { root in
let exactCount = try root["count"].requiredInteger(as: UInt64.self)
let optionalNote = try root.stringPresence(of: "note")
return (exactCount, optionalNote)
}
precondition(scoped.result.0 == 9_007_199_254_740_993)
precondition(scoped.result.1 == .null)
precondition(scoped.statistics[.workUnits] > 0)
```

Throwing String/JSONKey/Int brackets preserve literal scalar keys and typed array
indices. Required access never coerces or defaults. Presence distinguishes absent,
null and a non-null value; native model projection shares the scope's cumulative
meter. Double/Decimal getters require an explicit exact or rounded policy.
Escaped cursors invalidate at scope exit and retain no selected root/source.

```swift
import Foundation

let payload = try JSONCodablePayload(preserving: value)
let transported = try PropertyListEncoder().encode(payload)
let restored = try PropertyListDecoder().decode(JSONCodablePayload.self, from: transported)
let restoredValue = try restored.value()
precondition(restoredValue == value)
```

The explicit wrapper transports a fixed ASCII envelope with strict Base64 of
complete JSON, preserving exact decimals, scalar sequences, order and admitted
number lexemes. It retains validated bytes only. Automatic coding uses finite
balanced limits; explicit limits can tighten them. Foreign coder work remains
caller work. For SwiftData, use `@Attribute(.codable)` in your application target;
see the [executed disk example](Tools/MobileQualification/README.md).

## Compile and validate schemas offline

```swift
Expand Down
6 changes: 5 additions & 1 deletion Scripts/ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ 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'],
['swift', 'test', '--package-path', 'Tools/MobileQualification'],
['swift', 'run', '-c', 'release', '--package-path', 'Tools/MobileQualification', 'MobileQualification'],
['swift', 'test', '--package-path', 'Tools/MCPReadiness'],
['swift', 'run', '-c', 'release', '--package-path', 'Tools/MCPReadiness', 'MCPReadiness', str(output / 'consumer.json')],
['swift', 'test', '--package-path', 'Tools/Qualification'],
Expand Down Expand Up @@ -69,7 +71,7 @@ def check_result(command, code, log):
counts = [int(n) for n in re.findall(r'Test run with (\d+) tests? in[^\n]*passed', log)]
passed = code == 0
if command[:2] == ['swift', 'test']:
expected = [7] if R0_TESTS in command else [3] if 'Tools/MCPReadiness' in command else [1] if 'Tools/Qualification' in command else [1, 1, 2] if 'thread' in command else [9, 11, 12, 38, 78]
expected = [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 [1, 1, 2] if 'thread' in command else [9, 11, 12, 38, 86]
passed = passed and sorted(counts) == expected
if 'Tools/Qualification' in command:
probes = re.findall(r'^CAP_PROBE (\w+) (\S+)$', log, re.MULTILINE)
Expand All @@ -81,6 +83,8 @@ def check_result(command, code, log):
passed = sorted(p['name'] for p in d['products']) == ['JSON', 'JSONPatch', 'JSONSchema', 'JSONStreaming'] 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'}]
if command == ['swift', 'package', 'show-dependencies', '--format', 'json'] and passed:
passed = json.loads(log)['dependencies'] == []
if command[:2] == ['swift', 'run'] and 'Tools/MobileQualification' in command:
passed = passed and all(line in log.splitlines() for line in ['PUBLIC_BEHAVIOR passed fixtures=8', 'STREAM_CANCELLATION passed', 'DISK_PERSISTENCE passed containers=3 rows=8', 'MOBILE_QUALIFICATION passed cases=3'])
return passed, counts

def main():
Expand Down
Loading