Highlights
-
6.0 introduces explicit
@Inputand container roles, typed assisted child
factories, injectable multibindings, graph contract gates, owned async
preparation, on-demand services, and SwiftUI container lifecycle helpers.
See the breaking changes and upgrade actions below before updating from 5.x. -
Release-note extraction handles long Unicode sections on macOS's system
Bash without repeated full-string whitespace substitution. Exact content,
missing-section, and empty-section contracts are covered by subprocess tests. -
Apple trace owners amortize OS random generation in a bounded, lazy 1 KiB
batch while retaining random UUID v4 instance IDs. The owner lock protects
batch refill and consumption; disabled tracing allocates no batch. The
existing trace workloads and budgets are unchanged. See
profiling evidence. -
Runtime trace timing moves from mandatory CI/release gating to optional,
exact-SHA manual diagnostics. Raw measurements and rejection checks remain;
the reference budgets are not a 6.0.0 performance guarantee. Functional trace
tests, sanitizer checks, and the separate macro-performance gate are unchanged. -
Trace sinks execute outside on-demand cell locks. Initializing state is
installed before a start callback, and waiters recheck it after callbacks
to avoid lost wakeups. Same-thread, same-cell reentry from any trace callback
now diagnoses immediately; callbacks may safely resolve a different cell. -
Host phase observers may synchronously start or retry without losing the new
generation's cancellation handle or overwriting its phase. Cleanup barriers
are installed before notifications; replacements started from an idle or
ready notification wait for the previous container's close hook. -
Independent macro-performance workloads now cover an assisted factory with
8 static and 8 assisted inputs, 64-contributor multibinding, and 32-method mock
generation. RunTools/measure-macro-features.sh; CI archives all samples,
dimensions, workload versions, compiler and SHA provenance separately.
These v1 workloads are report-only until independently calibrated. They do
not replace, update, or count toward the composite-v2 release/trend baseline. -
On-demand Sendable safety: unrestricted deferred cells no longer claim
Sendable, even for a Sendable result, because arbitrary factory captures may
be unsafe. Keep ordinary on-demand containers on their isolation domain;
use eager storage or an explicitly main-actor container when appropriate.
For nonisolated async factories, generated code now uses a separate checked
handle requiring both a Sendable payload and an@Sendablefactory, including
transitive on-demand dependencies. Overrides preserve the same checks and
still skip unused factories. Compiler-negative and runtime controls cover
unsafe captures, payloads, regular/actor-isolated use and async dependency chains. -
Public collection metadata now uses checked
Sendableconformance and accepts
AnyKeyPath & Sendableat every construction boundary. Canonical member
literals remain source-compatible; callers building arrays explicitly must
preserve that intersection instead of erasing toAnyKeyPath. Mutable,
non-Sendable subscript captures are compiler errors, not silently transferable
metadata. The@Provide(collection:)canonical-member grammar is unchanged. -
Post-acceptance contract hardening:
- Async waiter cancellation no longer retains completed-request IDs across
scope resets. Caller cancellation is checked during actor-isolated
continuation registration; late handlers only remove live waiters. - Public API baseline schema 9 additionally records typealias RHS identities,
structure, actor isolation, Sendable and function effects. Compiler/consumer
mutation tests distinguish source breaks from qualification/format changes.
Generic RHS references use compiler-declared depth/index slots so direct
symbol-graph emission and serialized-module extraction compare identically.
Swift 6.2 omits function@Sendablefrom symbol graphs. The gate exports a
compiler interface from each built module and records Sendable positions
within alias type structure, including nested parameter/return/tuple
functions. It never infers a missing effect from source text or the baseline.
Nominal-scope lookup prevents same-named aliases from being conflated;
missing or ambiguous compiler-interface declarations fail closed.
Unknown nominal identities and ambiguous parameter metadata still fail
closed. Tests exercise both compiler paths and distinct nested generic slots.
This schema update changes no public declarations. - Release exact-revision consumers preserve preflight's annotated-tag/main
ancestry contract after normal main progress. Untagged initial dispatches,
rewritten history and mismatched checkouts still fail closed. - All seven READMEs now use the 6.0.0 package dependency and versioned
documentation, with a source-migration link for existing 5.x consumers.
- Async waiter cancellation no longer retains completed-request IDs across
-
Follow-up hardening of the c7 review candidate:
- Validation reads and hashes source bytes before reusing any AST digest,
including metadata-identical edits. Digest-cache version 7 invalidates old
records;metadata-hitnow means matching metadata and verified bytes. - Public API baseline schema 6 retains named actor attributes, isolation,
public setter availability, mutating methods/getters and nonmutating
setters (including subscripts). Real compiler/consumer fixtures cover
breaking changes and formatting-only controls; no public symbols were
added or removed by this baseline update. - Detached transient resolvers share typed dependency-only factory code
instead of recursively duplicating diamond dependency paths. Each call
still creates fresh transient values; overrides, lazy resolution and
escaped-handle ownership remain unchanged. - Async preparation uses iterative traversal for deep valid/cyclic graphs,
preserving declaration-order traversal and reverse close order. - Permanent async-scope close releases its stored factory captures. An
already-running operation retains its own captures until it returns. - SwiftPM DAG validation emits a comment-only generated Swift input so the
consumer compiler waits for the gate instead of racing and cancelling its
structured diagnostics on warm builds. Clang targets retain report-only
outputs; Xcode's multi-destination always-run gate remains unchanged. - Doctor verification and Graphviz use owned process groups and bounded
in-memory output tails (16 KiB per stream; merged for Doctor). Doctor keeps
its 300-second timeout; Graphviz now has a 30-second timeout. Group cleanup
has a 200 ms TERM grace then KILL, and signalled exit codes use 128+signal.
Descendants that deliberately leave the owned group are not forcibly
discovered or terminated. This is not a sandbox for untrusted commands.
Closed caller standard streams are normalized before spawn so child
output remains correctly separated or merged.
These changes do not migrate standalone products, approve a new performance
baseline, or constitute release approval. Upgrading existing 5.x consumers
is not a prerequisite for publishing the 6.0 library.
- Validation reads and hashes source bytes before reusing any AST digest,
-
SampleApp resolves its local dependency and DAG plugin using the checkout
directory's normalized SwiftPM identity, matching the other examples.
Renamed-checkout CI now tests and runs SampleApp as well as building the
SwiftUI examples; no canonicalInnoDIdirectory name is required. -
Accepted RFC 0006
following explicit owner approval on 2026-09-24, after the promotion PR's
seven-day cooldown. This freezes the 6.0 assisted-factory,@Input, explicit
container-role and multibinding syntax, including the documented replacements
for 5.x declarations. It records design acceptance, not a GitHub PR review,
merge, tag or release approval. Publication is established by the exact-SHA
Release Gate and immutable GitHub Release, not by RFC acceptance. -
Re-audited all 46 excellence requirements and 25 follow-up findings against
code candidate6332864ea83743fd5fec99c95b98a91b1b06ae8b. A clean Swift 6.4
strict coverage run passed 355 tests in 37 suites with package line coverage
90.28% andInnoDIMacros90.75% (floor 90.70%). Public API, graph schema v6,
DocC, localized README, link, validation-escape-hatch, fatal-trap, alias and
runtime trace performance contracts also passed. The synchronized exact
branch HEAD is rechecked by the release-validation matrix and consumers.
These are historical candidate measurements, not the final release proof. -
Hardened the 6.x release-candidate validator so publication fails closed
unless RFC 0006 has exactly oneAcceptedstatus in both its authoritative
document and the RFC index. Pending, missing, duplicate, and inconsistent
records are covered by executable release-contract tests. -
6.0 breaking ownership correction (R02):
Lazy<T>andProvider<T>
no longer exempt dependency cycles. Local cycles are compile errors even
withvalidateDAG: false; global DAG checks also include deferred edges.
Migrate mutual references by extracting shared state or restructuring the
graph. Acyclic forward references, transient re-entry, and escaped handle
lifetime remain supported. No explicit scope-close API is introduced.
The earlier candidate measurements immediately above predate this correction;
see final hardening. -
Migration publication and rollback now use a preserving atomic exchange
(R01/R09), never an unconditional overwriting rename. Every displaced entry
remains at a reported recovery path, including after success, so late writes
through open editor descriptors are retained. A conflict exits nonzero and
requires review of source and recovery paths; no unsafe restore is attempted.
POSIX source modes are restored independently of umask. Doctor schema v3 adds
recoveryPaths; migration's read-only report remains schema v1. -
DIContainerHostOwner.close()releases stored factory/close captures before
suspension, without clearing a reentrant new generation's callbacks (R03). -
Subgraph retry now reserves library-owned scopes before checking states and
commits all affected generations before release (R04/R05). Selected custom
DIAsyncPreparingproviders fail withnonTransactionalProviderbefore any
mutation; prepare/close support remains.DIAsyncScopestatus/retry/reset/close
are explicitly asynchronous, including calls made from actor-isolated code.
Concrete, existential, and generic reset calls now share the same semantics. -
Added graph explainability commands:
--whytraces a shortest root path,
--dependentsreports reverse impact,--unusedfinds containers outside
every rooted graph, and--diffcompares two schema-v6 JSON artifacts.
--diff ... --check-contractturns that comparison into a CI gate: unchanged
contracts exit 0 and any scope, node, or edge drift, including assisted input,
assisted-factory ownership, or ordered contribution changes, exits 5 while
preserving the human-readable diff. Schema v6 treats canonical factory
parameter wiring and fixed/assisted child binding pairs as contract. It
additionally records explicit collection kind, keys, order, contributor IDs,
and contributor lifetimes. It rejects earlier schemas, missing binding
metadata, and malformed collection contracts rather than treating them as
unchanged. Regenerate older baselines before
comparing them with this candidate. Query selectors now check container and
provider namespaces together. Cross-namespace collisions list both candidate
sets and requirecontainer:orprovider:; exact graph IDs remain stable,
and provider dependents follow canonical binding IDs rather than parameter
labels. Fixed-child and assisted-factory queries also follow canonical parent
input bindings per mount. JSON validation rejects dangling/foreign references,
invalid ownership, and incomplete ordinary child input coverage before diffing,
including identical invalid inputs. -
Connected generated providers to opt-in runtime tracing. Container,
component, override, on-demand, transient, and async paths now carry the
canonical schema-v6 provider identity, container owner, and generation;
start/terminal, override, cache-hit, and wait relationships are emitted
automatically. The disabled default still avoids UUID/event/buffer
allocation, events remain metadata-only, and opaque work started inside a
service is deliberately outside the trace boundary. -
Completed generated-mock stub preflight across properties, ordinary returns,
untyped and typed throwing functions, and generic handlers. Setup state is
independent from optional storage, so an explicitly stubbednilis not
reported as missing. Unnamed parameters now receive legal body identifiers;
unsupported generic typed throws and static properties fail at the source
attribute without emitting a partial conformance. -
Added generation-aware reset to actual generated mocks.
.callsatomically
closes and returns the current call-history snapshot while preserving stubs;
.allalso returns every stub to its missing state.Sendablemocks use one
shared critical region, and@MainActormocks use actor serialization, so a
racing call belongs to exactly one generation. -
Added
InnoDI-Migrate --reportfor deterministic schema-v1 JSON inventories
before migration writes. Reports expose paths, stable codes, counts, status,
and diagnostics without including original or migrated source bodies. -
Removed the superseded underscored assisted-factory SPI after the public
@Input(.assisted),@AssistedFactory, and@SubContainerFactorysurface
replaced its same-target, cross-module, runtime-isolation, and override
evidence. The SPI was never covered by SemVer and no recorded pilot remains
pinned to it. -
The public assisted-factory bridge now preserves
@MainActoron its
initializer, call, and override-application closure. A same-target Swift 6
strict-concurrency fixture guards the exact Xcode consumer shape that would
otherwise reject override forwarding as a non-Sendable actor crossing. -
Added public
@Multibindingfor one injectable deterministic ordered
collection from explicit local synchronous providers with the same written
type. Macro, serialized validation, graph-v6, and strict external-consumer
tests cover invalid contributors, injection, shared/transient lifetime
behavior, contributor order, and overrides. The superseded underscored SPI
has been removed after public consumer migration. -
Verified the public RFC 0006 runtime and SwiftUI host pilot in InnoSample
commitec88716against validated InnoDI code candidate
f1a3eaccf19bfc43164de3621c9197c731d92342. The People route passes the
consumer's full Xcode 27 gate, proves per-child shared-state isolation plus
overrides, and replaces its manual state wrapper withDIContainerHost. -
Added two more committed consumer pilots against that code candidate. BlPia
c12560dpasses Doctor over 160 Swift files, an unchanged second migration
pass, DAG validation, 10 test schemes, and a generic iOS/watch build; the
strict hierarchy gate also corrected seven manually provided containers from
componenttolocalownership. Lynceus3edb77bpasses Doctor over 81
Swift files, an unchanged second pass, a real
two-container full-root DAG, 41 tests, and its macOS build. Mulbyul was tested
without source changes and is deliberately not counted as a committed pilot. -
Refreshed the consumer boundary for the T42 candidate in isolated clones.
InnoSample passes exact resolution, DAG, Remote tests, leaf/root features and
generic iOS/watch builds. BlPia passes layer/feature/app tests and iOS build
after explicitly linking trace runtime support into static test bundles.
Lynceus passes format, Tuist sync, macOS build and all tests; Doctor correctly
marks its helper-based Tuist target mapping analysis-incomplete instead of
healthy. Mulbyul committed HEAD092ff951remains test-only: its isolated
Layersbuild reaches the expected legacy@Provide(.input)source break,
while read-only Doctor reports 481 files, one proposal and 11 errors without
applying a change. The original Mulbyul and mixed BlPia checkouts are
preserved. -
Hardened migration and workspace analysis from real-consumer evidence:
ambiguous unqualified 6.0 vocabulary now fails closed (dc34d14), Doctor
recognizes direct Tuist package workspaces (ac1124b), and skipped hidden
files no longer suppress following source siblings during full-root graph
discovery (28a95a5). -
Added isolated Thread Sanitizer and Address Sanitizer suites to both the main
validation workflow and the SHA-bound release gate. They run every applicable
in-process strict-concurrency test from separate scratch paths so sanitizer
state cannot be reused across lanes; separately spawned fresh-consumer builds
remain covered by the exhaustive and compatibility jobs. -
Hardened Xcode 27 / Swift 6.4 release preparation: external-consumer
diagnostics now preserve exact toolchain-specific compiler output, while the
public API guard tracks only source-authored product declarations instead of
SDK symbols re-exported by toolchain-specific SwiftUI symbol graphs. The
schema-v4 API baseline also preserves a per-parameter default-presence vector:
removing a default now fails even when the symbol identity does not change.
Declaration formatting and default-expression values are not API identity;
executable compiler/consumer fixtures verify the omitted-argument contract.
Macro defaults are recovered even when older compiler symbol graphs omit
functionSignature; missing metadata is not silently treated as no defaults.
The
coverage collector now accepts both the combined package test bundle used by
earlier toolchains and Swift 6.4's per-target test bundles, including public
executable entry points without lowering any checked-in floor. -
The 6.0 vocabulary migrator and examples now emit a required
role:label
with a named, string-backedContainerRoletoken and the established
mainActor: trueoption. This avoids a Swift 6.2.3 compiler signal 11 while
matching a public enum argument in the multi-role attached
@DIContainerRoleexpansion, without dropping Xcode 26.2 compatibility from
the release gate. Arbitrary strings fail with a stable InnoDI diagnostic. -
Added owned on-demand and async preparation scopes, a SwiftUI container host,
concurrency-safe public testing support, explicit cross-module ordered/keyed
provider collections, schema-v6 provider contract queries, metadata-only
bounded runtime tracing, and a read-only-firstInnoDI-Doctorworkflow for
the 6.0 candidate. -
Added
@Provide(collection:)closed metadata for factory-built ordered,
keyed, value, and provider collections. Key identity, order, canonical
contributor, and declared contributor lifetime are contractual across graph
artifacts; explicit empty is valid, duplicates fail, and no factory-body or
module discovery is performed. -
Async preparation now rejects already-cancelled waiters before factory start,
reports request and owned-operation cancellation separately from failure,
and retries a failed selected child plus its downstream in fresh generations
while preserving ready explicit parent dependencies. -
Generated override fallbacks now parenthesize precedence-sensitive raw factory ASTs, full-source
validation rejects out-of-order childbindings:at the first mismatching
key path, and async overrides no longer resolve dependencies used only by a
bypassed live factory. -
Feature-root helpers now include an identity-taking
DIContainerHost
overload, hosted content receives an explicit lifecycle handle through the
SwiftUI environment, and#PreviewWithContainerconstructs lazily through
the same generation owner. Existing direct and manual host APIs remain. -
Added explicit
@Provide(effect: .sideEffect)metadata and generated
Overridescompleteness reporting. Test and preview targets can use
InnoDITestingstrict preflight to reject missing effect overrides before a
live factory runs; recording mode returns the same deterministic report.
Unmarked opaque factories remain unclassified, and production construction
does not enable this opt-in policy globally.
Breaking and Behavior Changes
- New 6.0 vocabulary: replace
@Provide(.input)with@Input. Replace
@DIComponent,@DIHierarchyRoot, and the former@DIContainer(root:mainActor:)
options with@DIContainerRole(role: ContainerRole.component)for mountable
features,ContainerRole.rootfor roots, orContainerRole.localfor local
isolation. AddmainActor: trueto the role macro when needed. Each
declaration uses one container macro; ordinary containers continue to use
@DIContainer. - Deferred ownership:
LazyandProviderno longer make dependency cycles
valid. Local cycles are rejected even withvalidateDAG: false, and the
global DAG includes deferred edges. Ordinary on-demand storage is not
Sendable; nonisolated async dependency handles require a Sendable payload
and an@Sendablefactory. Keep non-Sendable state in its isolation domain. - Collection metadata: explicitly assembled key-path arrays must preserve
AnyKeyPath & Sendable. The old assisted-factory and multibinding SPI is
removed; use@Input(.assisted),@AssistedFactory,@SubContainerFactory,
and@Multibinding. - Graph and tooling contracts: graph JSON is schema v6 and older baselines
are rejected. Doctor schema v3 adds migration recovery paths; its read-only
migration inventory remains schema v1. Ambiguous graph selectors require
container:orprovider:. A contract diff exits 5 on semantic drift. - Owned async lifecycle:
DIAsyncScopestatus, retry, reset, and close are
explicitly async. Transactional subgraph retry rejects selected custom
DIAsyncPreparingimplementations before mutation; prepare and close remain
supported. Applications own shutdown and should await close hooks. - Migration safety: applying or rolling back a migration preserves each
displaced entry at a reported recovery path, even after success. Conflicts
exit nonzero instead of overwriting late editor changes. Review recovery
paths before removing them. - Tracing and mocks: generated initializers and override operations add a
defaulted_innoDITrace:parameter. Trace decoders must accept owner,
generation, origin, related identities, and wait events. Same-cell synchronous
trace callback reentry is diagnosed.@GenerateMockremains experimental;
6.0 does not make its generated helper layout a stable API.
Upgrade Actions
-
Read the 5.x to 6.0 migration guide
and the accepted RFC 0006
before changing the package requirement tofrom: "6.0.0". -
From an InnoDI 6.0 checkout, run these read-only checks first, replacing
/path/to/consumerwith the consumer's package or source-tree root:swift run InnoDI-Doctor --root /path/to/consumer swift run InnoDI-Migrate --root /path/to/consumer --check swift run InnoDI-Migrate --root /path/to/consumer --report
--checkexits 1 when migration is required; inspect the report rather than
treating that result as a tool crash. Review the proposed vocabulary changes,
commit or back up consumer work, then replace--checkwith--writeonly
when ready to apply them. Resolve dynamic/conflicting sites manually and
inspect any reported recovery paths. -
Replace cyclic deferred wiring, removed SPI, and erased collection key paths.
Rebuild actual consumers under complete strict concurrency with warnings as
errors; validate factory captures as well as their result types. -
Regenerate both graph baselines with the same 6.0 tools before enabling
--diff ... --check-contract. Update JSON readers and trace event decoders
for the schema and metadata changes above. -
Exercise async cancellation/retry/close, on-demand overrides, and SwiftUI
host replacement in each adopting application. Existing products may remain
pinned to 5.x; publishing the library does not migrate them automatically.