Skip to content

Swift 6.4 toolchain, cross-platform gating, and BrightDigit CI template (#4) - #15

Merged
leogdion merged 42 commits into
v0.1.xfrom
v0.1-scaffold
Jul 30, 2026
Merged

Swift 6.4 toolchain, cross-platform gating, and BrightDigit CI template (#4)#15
leogdion merged 42 commits into
v0.1.xfrom
v0.1-scaffold

Conversation

@leogdion

@leogdion leogdion commented Jul 29, 2026

Copy link
Copy Markdown
Member

Five commits against #4: source prerequisites, the house CI scaffolding, then three rounds of fixes driven by real CI runs.

1. Toolchain + gating (ccce64f)

The BrightDigit CI template builds on Linux, Windows, Android, and wasm. Those legs could never have gone green: FCPKitMediaTools imported CoreMedia/AVFoundation unconditionally and exposed CMTime in its public API.

swift-tools-version 6.1 → 6.4 plus .swift-version. I expected 6.4 to flip on Swift 6 language mode and cascade strict-concurrency errors across the ~69 non-Sendable structs. It did not — no swiftLanguageMode(.v5) pin needed.

Gating via canImport(CoreMedia) / canImport(AVFoundation), whole declarations:

Gated Available everywhere
VideoMetadata (duration: CMTime) VideoMetadataError
VideoMetadataExtractor (AVAsset-based) generateUID, frameDurationFromFrameRate
MulticamXMLBuilder currentTimestamp, formatFileURL, generateFormatName
FCPXMLUtilities.cmTimeToFCPXMLDuration, .generateAssetSignature

fcpxml-generator is @main, so gating it out entirely would leave a target with no entry point. The non-Apple branch supplies an @main that writes to stderr and exits 1.

DocsAGENTS.md and ADR 0002 said "Write new tests in XCTest for now." Now reversed: new tests use Swift Testing per this guide. Existing XCTest suite untouched. Relevant to #5, whose spine-order test wants withKnownIssue { }.

2. CI template (c98aff0)

Adapted from SyndiKit: main workflow + check-unsafe-flags, codeql, cleanup-caches, swift-source-compat, both claude workflows; .github/actions/setup-tools, .github/matrices, dependabot.yml; Scripts/lint.sh + header.sh; .swiftlint.yml, .swift-format, .mise.toml, .spi.yml, .periphery.yml, codecov.yml.

⚠️ Worth reviewing: the matrix is 6.4-only. SyndiKit declares swift-tools-version:5.10 (plus a Package@swift-6.0.swift variant) — that low floor is what lets its matrix span 5.10→6.4. FCPKit declares 6.4, so every older leg cannot parse Package.swift. Dropped: Ubuntu 5.10/6.0/6.1/6.2/6.3, Windows 6.3, Android 6.3, macOS Xcode 15.4/16.4/26.6, and the entire source-compat sweep. Each trimmed spot is commented. visionOS is dropped separately — Package.swift does not declare it.

Restoring coverage means lowering the tools-version or adding Package@swift-X.swift variants.

3–5. Fixes from CI (8cac082, d7c287c, 8f0e242)

Each of these was found by an actual CI run, not by inspection:

  • XMLParser on Linux lives in FoundationXML, not Foundation#if canImport(FoundationXML) shim in XMLTree.swift. This corrects an earlier claim of mine that FCPXMLDiff was portable because it "imports only Foundation" — an import list does not imply portability.

  • Test-target gatingMulticamXMLBuilderTests and TypedGenerationTests import AVFoundation. My first Linux check ran swift build rather than --build-tests, so it never compiled the test target and missed this.

  • testValidateThrowsWhenDTDMissing asserted dtdNotFound, but validate() checks for xmllint (DTDValidation.swift:104) before the DTD lookup (line 114). The Linux container has no xmllint. The test now accepts either error and still fails on any other kind.

  • Codecov uploads 404 ("Repository not found" — repo not registered / no CODECOV_TOKEN) and fail_ci_if_error: true failed the macOS leg. Now false with a comment. Flip back once Codecov is configured.

  • wasm SDK — legs failed inside XMLCoder with "compiled module was created by an older version of the compiler"; the pinned 2026-06-15 snapshot predates the nightly container. Bumped wasm + Android SDKs to 2026-07-22 (newest published; checksum recomputed from the downloaded bundle).

    Note SyndiKit and Spinetail both pin that same stale 2026-06-15 SDK and set ENABLE_WASM=false rather than chase it. If the bump does not hold, gh variable set ENABLE_WASM --body false is the house fallback.

Verification

  • macOS: xcrun swift test58 tests, 0 failures; schema-completeness gate exit 0
  • Real Linux (swiftlang/swift:nightly in Docker, swift build --build-tests): builds clean, FCPKitTests-test-runner links. CI runs 54 of 58 tests there — the 4 AVFoundation-dependent ones gate out correctly.
  • This also settled the open CGSize question: no gating needed, Linux Foundation vends it.
  • All workflow YAML and matrix JSON parse

🔴 Expect the lint job to fail

Measured against the house config, not estimated:

Tool Violations
SwiftLint (--strict) 1,522
swift-format (--strict) 6,060

Top rules: indentation_width (463), type_contents_order (389), trailing_whitespace (185), explicit_acl (145), one_declaration_per_file (93).

Deliberately not in this PR — it would bury the toolchain and gating work under thousands of reformatted lines. Suggested split: #4c auto-fixable formatting (swiftlint --fix, swift-format --in-place), then #4d judgement-call rules (explicit_acl, one_declaration_per_file, missing_docs).

Deferred

#4)

Prerequisite for adopting the BrightDigit house CI template, whose matrix
includes Ubuntu, Windows, Android, and wasm legs. FCPKitMediaTools imported
CoreMedia and AVFoundation unconditionally and exposed CMTime in its public
API, so those legs could not have compiled.

- swift-tools-version 6.1 -> 6.4; add .swift-version. No language-mode pin was
  needed: 6.4 keeps the default language mode, so no concurrency diagnostics
  surfaced across the ~69 non-Sendable model structs.
- Gate on canImport(CoreMedia)/canImport(AVFoundation): VideoMetadata,
  VideoMetadataExtractor, MulticamXMLBuilder, and the two CMTime/VideoMetadata
  members of FCPXMLUtilities. VideoMetadataError and the four pure-Foundation
  helpers stay available everywhere.
- fcpxml-generator keeps an @main on non-Apple platforms that exits 1 with a
  clear message, so the executable target still builds in cross-platform CI.
- AGENTS.md and ADR 0002 now direct newly authored tests to Swift Testing; the
  existing XCTest suite is unchanged and its migration remains deferred.

Contributes to #4.
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 40291739-4d32-4478-9220-a7807b05971a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@leogdion leogdion mentioned this pull request Jul 29, 2026
5 tasks
Brings over the house scaffolding: the main multi-platform workflow plus
check-unsafe-flags, codeql, cleanup-caches, swift-source-compat, and the two
claude workflows; .github/actions/setup-tools, .github/matrices, dependabot;
Scripts/lint.sh + header.sh; .swiftlint.yml, .swift-format, .mise.toml,
.spi.yml, .periphery.yml, codecov.yml.

Adapted from SyndiKit (the most recent BrightDigit package) rather than
Spinetail. One structural difference matters: SyndiKit declares
swift-tools-version 5.10 with a Package@swift-6.0.swift variant, so its matrix
spans Swift 5.10-6.4. FCPKit declares 6.4, which older toolchains cannot parse
at all, so every leg here is pinned to 6.4 — nightly containers on
Linux/Windows/Android and Xcode 27 on macOS. The trimmed spots are commented so
the coverage can be restored if the tools-version is ever lowered.

visionOS is dropped from the macOS matrix because Package.swift does not
declare that platform.

The lint job will fail on its first run: 1522 SwiftLint violations (mostly
indentation_width, type_contents_order, explicit_acl, one_declaration_per_file)
and 6060 swift-format violations against the existing sources. Working that
backlog down is deliberately a separate change.

Contributes to #4.
@leogdion leogdion changed the title Swift 6.4 toolchain + cross-platform gating for FCPKitMediaTools (#4a) Swift 6.4 toolchain, cross-platform gating, and BrightDigit CI template (#4) Jul 29, 2026
leogdion added 4 commits July 29, 2026 11:31
Both fixes come from the first real CI run on this branch.

XMLTree.swift uses XMLParser, which on Linux lives in FoundationXML rather than
Foundation. This made the Ubuntu leg fail to build FCPXMLDiff. Verified fixed
against swiftlang/swift:nightly in Docker — the whole package now builds clean
on aarch64 Linux, which also confirms the earlier canImport gating works and
that CGSize needs no gating.

Codecov uploads 404 with "Repository not found" because FCPKit is not yet
registered / CODECOV_TOKEN is unset, and fail_ci_if_error was true. Set to
false across the workflow with a comment; flip back once Codecov is configured.

Contributes to #4.
The Ubuntu leg failed with "no such module 'AVFoundation'" in
MulticamXMLBuilderTests and TypedGenerationTests. Both exercise
MulticamXMLBuilder / VideoMetadata, which are already gated on CoreMedia, so
the test bodies are now gated the same way.

My earlier Linux check ran `swift build` rather than `swift build --build-tests`,
so it never compiled the test target and missed this. Re-verified with
--build-tests against swiftlang/swift:nightly: FCPKitTests-test-runner links
clean on Linux.

Contributes to #4.
Two more findings from CI. The Ubuntu leg now compiles and runs — 54 of 58
tests, with the 4 AVFoundation-dependent ones correctly gated out.

testValidateThrowsWhenDTDMissing asserted dtdNotFound, but validate() checks for
xmllint (DTDValidation.swift:104) before looking up the DTD (line 114). The
Linux CI container has no xmllint, so it throws xmllintUnavailable first. The
test now accepts either error — both prove validation refused to run — and still
fails on any other error kind.

The wasm legs failed inside XMLCoder with "compiled module was created by an
older version of the compiler": the pinned 2026-06-15 wasm SDK predates the
nightly container's compiler. Bumped the wasm and Android snapshot SDKs to
2026-07-22 (newest published; checksum recomputed from the downloaded bundle).

Note SyndiKit and Spinetail both pin the same stale 2026-06-15 SDK and set
ENABLE_WASM=false rather than chase it, so ENABLE_WASM=false remains the
fallback here if the bump does not hold.

Contributes to #4.
The checksum in the previous commit was computed from a curl download that
omitted -L, so it hashed a redirect body rather than the artifact. Both are
92788382 bytes, which made the wrong value look plausible. Re-downloaded with
-L; the correct SHA-256 is 72ef761b..., matching what CI computed.

Android is unaffected — that leg passes no checksum.

Contributes to #4.
@leogdion leogdion added the v0.1.0 FCPKit v0.1.0 milestone work label Jul 29, 2026
leogdion and others added 20 commits July 29, 2026 13:26
The wasm legs got past the SDK checksum and then failed to compile:

  Sources/FCPXMLDiff/DTDValidation.swift:130: cannot find 'Process' in scope

WebAssembly has no process model, so Foundation.Process does not exist there,
and validate() shells out to /usr/bin/xmllint. The implementation is now behind
#if !os(WASI); the wasm build gets a stub with the same signature that throws
FCPXMLValidationError.xmllintUnavailable — the existing error for "the tool
isn't here", already reachable on any host without xmllint. No API or error
cases change, and no other Process use exists in Sources.

This is the same root cause as the earlier Linux test failure seen from another
angle: DTD validation is inherently a subprocess feature.

Contributes to #4.
Reverting my own bump. Testing the SDK against the container CI actually uses
(swiftlang/swift:nightly-6.4.x-noble, which reports 6.4-dev) shows the original
2026-06-15 pin is correct: the 2026-07-22 SDK is *newer* than that container and
fails with "compiled module was created by a newer version of the compiler".

The earlier reasoning was wrong in a way worth recording. The first wasm failure
said the module was created by an *older* compiler, and I read that as the pin
being stale. It was not: that run predated the FoundationXML/AVFoundation fixes
and the error came from a different toolchain pairing. The `swiftlang/swift:nightly`
image I tested against has since moved to 6.5, so it disagrees with the SDK in
the opposite direction. Only the pinned 6.4.x container is authoritative here.

With the correct SDK, the WASI gate from the previous commit is confirmed working
-- DTDValidation.swift no longer appears -- and the build advances to a new
limitation: `.atomic` writes are unavailable on WASI, which has no temporary
files. Six sites in FCPXMLDiffCLI now route through a small
Data.writeAtomicallyIfSupported helper. The generator's atomic write is already
inside the canImport(AVFoundation) branch and never compiles for wasm.

Contributes to #4.
Consolidates the corrections log into a single in-repo file that is the source
of truth for how to work in this repo, with a self-describing header so any
agent can follow the convention without external context.

The three entries from .claude/CORRECTIONS.md are carried over verbatim in
meaning, plus this session's directives (Swift Testing for new tests, canImport
gating for non-Apple platforms, verify cross-platform claims in the real target
environment, use the newest sibling repo as the scaffolding template, prefer PRs
over merging).

One rule changed rather than moved: the old file was strictly append-only
("never rewrite, reorder, or delete"). The new file instead requires updating or
removing a stale line when a directive supersedes it, so the log always reads as
the current rule set. Both the new header and the memory note record that shift
explicitly rather than letting it happen silently.

AGENTS.md gains a "Memory & Corrections Convention" section pointing future
sessions at the file first. Updated .claude/memory/ references to the old path.
Set the repo variable ENABLE_WASM=false, matching SyndiKit and Spinetail, and
record the reasoning in the workflow next to the branch that reads it.

The wasm legs cannot pass with the template as written: the container tag
`swiftlang/swift:nightly-6.4.x-noble` moves while the wasm SDK URL is pinned to
a fixed snapshot date, so the SDK's prebuilt Swift.swiftmodule fails to load
inside XMLCoder before reaching any FCPKit source. The mismatch reported in both
directions depending on which nightly a given runner had cached -- "older" on
GitHub's x86_64 runner, "newer" locally on arm64 with the same tag -- so no
pinned SDK date is stably correct.

Digest-pinning was considered; swiftlang/swift publishes no dated 6.4.x tags,
only moving OS variants, so it would mean hand-pinning a sha256 that goes stale
on its own schedule. Not worth it against a nightly.

Note the two FCPKit-side wasm blockers found along the way are already fixed and
stay fixed: DTD validation is gated behind #if !os(WASI), and atomic writes go
through Data.writeAtomicallyIfSupported.

Contributes to #4.
The digest experiment finished after ENABLE_WASM was already set, and its result
is worth keeping: it reproduced CI's failure exactly and identified the real
variable as architecture, not image drift.

`swiftlang/swift:nightly-6.4.x-noble` ships different Swift builds per arch —
arm64 carries 80702ec.., amd64 carries ef761e56.., which is older than the
2026-06-15 wasm SDK. My Mac pulls arm64, GitHub runners are x86_64, which is why
an identical tag + SDK built locally and failed in CI. Running CI's exact amd64
digest locally reproduced the "older version of the compiler" error.

So pinning the amd64 digest would pin the broken pairing, not fix it.
ENABLE_WASM=false stands.

Also logs the general lesson in .claude/agent-notes.md: pass --platform
linux/amd64 when verifying in Docker, or a local pass proves nothing about CI.

Contributes to #4.
Scripts/lint.sh built the package to "check for compilation errors", but
linting is syntactic: swiftlint and swift-format need no build products,
and CI already builds and tests. The build step also broke locally, since
bare `swift` resolves to swiftly's default toolchain, which has no 6.4
release to match .swift-version. Remove the step rather than hardcoding
`xcrun`, which would not work on Linux.

Drop the `targets` key from .periphery.yml; Periphery 3.x derives SPM
targets from Package.swift and warned the key was invalid.

Also includes the in-place swift-format/swiftlint --fix pass that
Scripts/lint.sh applies by design.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
FCPXML.swift (581 lines), FCPXMLExtended.swift (693) and
FCPXMLMissingElements.swift (413) held 69 public types between them,
tripping file_length, file_name and one_declaration_per_file. Split them
into Sources/FCPKit/{Document,Resources,Timeline,Elements,Adjustments,
Metadata}/ grouped by the DTD's own structure.

Type bodies are copied verbatim. XMLCoder emits child elements in
CodingKeys declaration order, and ordered DTD content models depend on
that order (AGENTS.md:67-70), so no member was reordered here.

Verified: re-encoding all four TestData fixtures produces byte-identical
XML before and after the split (matching SHA-1s), which covers the child
ordering invariant the schema-completeness report does not measure.
58 tests pass.

one_declaration_per_file: 93 -> 27; total violations 944 -> 870.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each of these files paired a report type with its analyzer and renderer,
tripping one_declaration_per_file and file_name (the file was named for
one type but declared several).

File-private helpers (FindingKey, TreeParserDelegate, Inventory) stay in
the file of the type that uses them; `private` at top level is file
scoped, so moving them out would not compile.

one_declaration_per_file: 27 -> 0; total violations 870 -> 846.
58 tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SwiftLint's type_contents_order wants subtypes declared before instance
properties and initializers; these types declared CodingKeys last.

The CodingKeys enums are moved as opaque blocks -- no `case` line was
touched or reordered. XMLCoder emits child elements in CodingKeys
declaration order and ordered DTD content models depend on it
(AGENTS.md:67-70), so case order is load-bearing.

Verified three ways: all 349 case lines across the 71 changed files keep
their exact sequence; re-encoding the four TestData fixtures still
produces XML byte-identical to the original pre-refactor output; 58
tests pass.

type_contents_order: 389 -> 0; total violations 846 -> 464.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Move FCPXMLError, FCPXMLVersionCompatibility and VideoMetadataError into
their own files.

FCPXMLVersion.swift and VideoMetadata.swift were split by hand rather
than by script: the former carries three top-level extensions (a scripted
split dropped them and broke the build), and the latter nests its main
type inside `#if canImport(CoreMedia)`. The extensions on FCPXML and
FCPXMLParser move alongside FCPXMLVersionCompatibility, since that is the
type whose behavior they add.

Verified no declaration was lost in this or the two earlier splits by
diffing declaration inventories against the pre-split commits.
58 tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
explicit_acl and explicit_top_level_acl require every declaration to
state its access level. Adds `internal` to 146 declarations, mostly
XCTest classes and test methods plus the CodingKeys enums in the model
files.

Driven by SwiftLint's own violation locations rather than a regex sweep,
so only genuinely implicit declarations were touched.

Verified emitted XML is still byte-identical to the pre-refactor
baseline; 58 tests pass.

explicit_acl: 146 -> 0, explicit_top_level_acl: 13 -> 0;
total violations 458 -> 299.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
multiline_arguments_brackets wants the closing bracket of a multi-line
argument list on a line of its own. Confirmed swift-format preserves this
form rather than reverting it, so the two tools agree here.

The 15 remaining violations of this rule are inside multi-line string
literals holding XML fixtures, where SwiftLint misreads XML indentation
as Swift code.

58 tests pass; total violations 299 -> 240.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
conditional_returns_on_newline wants `guard x else { return }` to place
the return on the next line. Confirmed swift-format preserves the
expanded form.

58 tests pass; conditional_returns_on_newline: 16 -> 0;
total violations 240 -> 224.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds doc comments for the 21 undocumented public declarations flagged by
missing_docs: FCPXMLParser and its parse/encode methods, VideoMetadata's
stored properties and memberwise init, and two public initializers.

The FCPXMLParser doc notes that encoding emits children in CodingKeys
declaration order and that `.sortedKeys` is deliberately unset, since
ordered DTD content models depend on it.

58 tests pass; missing_docs: 21 -> 0; total violations 224 -> 203.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- force_unwrapping: use try XCTUnwrap in tests instead of `!`
- xct_specific_matcher: XCTAssertNil / XCTAssertEqual over XCTAssertTrue
- non_optional_string_data_conversion: Data(string.utf8) over
  data(using:) ?? Data()
- convenience_type: caseless enums for static-only types, including the
  three @main entry points
- multiline_parameters: one parameter per line in Resources.init

Verified both CLIs still run after the @main conversion: fcpxml-diff
reports no structural loss on the fixtures, fcpxml-generator prints help.
58 tests pass; total violations 203 -> 192.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Extracts summaryRow/lossSentence helpers in the schema-completeness
renderer, a describe() helper for the generator's per-video output, and
splits two long interpolated strings.

The asset-signature seed is now built by explicit concatenation with a
comment noting the order is part of the signature.

Verified the schema-completeness report output is byte-identical to the
baseline and fcpxml-generator still prints usage. 58 tests pass.

Remaining line_length violations are single string literals (inline XML
fixtures) that swift-format cannot break.

total violations 192 -> 185.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
FCPXMLNodeEncoding.swift held 69 DynamicNodeEncoding conformances in one
432-line file whose name matched no type in it, tripping file_name and
file_length. Each conformance now sits in the file of the type it
extends; the shared fcpNodeEncoding helper moves to FCPNodeEncoding.swift
and becomes internal so it stays visible across files.

Also splits AssetClipEditing.swift into AssetClipEditingError.swift and
AssetClip+Editing.swift.

These conformances decide attribute-vs-element encoding, so bodies were
copied verbatim and the result verified: emitted XML for all four
fixtures is still byte-identical to the original pre-refactor baseline.
58 tests pass.

file_name: 2 -> 0; total violations 185 -> 182.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- file_types_order wants supporting types before the main type, so the
  file-private helpers (TreeParserDelegate, Inventory, FindingKey) move
  above the types that use them.
- type_contents_order wants type properties and type methods before
  instance members: hoists FCPXMLVersion's static constants above
  rawValue/init, and the private static helpers in FCPXMLVersion and
  AssetClip+Editing above the instance methods.

58 tests pass; type_contents_order: 7 -> 0; total violations 182 -> 174.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
generateMulticamDocument was a single 273-line function built around one
deeply nested FCPXML literal. It now derives its computed values into a
private BuildContext and delegates to focused sub-builders: buildAssets,
buildFormats, buildMedia (itself split into the combined, per-side and
multicam entries) and buildLibrary.

Literal bodies were moved verbatim. Verified with a temporary harness
that the generated multicam document is structurally identical before and
after, comparing the encoded XML with the random UIDs masked.

Largest function 273 -> 70 lines; builder violations 9 -> 4.
58 tests pass; total violations 173 -> 174 (one file_length surfaced by
the added function signatures).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The test suite and CLI help embed FCPXML fixtures as multi-line string
literals. SwiftLint measured the XML's own indentation and line length as
if it were Swift, producing 99 violations that could only be "fixed" by
reindenting the fixtures — which would corrupt the test data, since these
strings are compared byte-for-byte.

Both rules have an option for exactly this case:
  indentation_width.include_multiline_strings: false
  line_length.ignores_multiline_strings: true

Thresholds are unchanged; this narrows what the rules inspect, it does not
relax them. Also removes a stray blank line before a closing brace.

58 tests pass; total violations 174 -> 74.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
leogdion and others added 16 commits July 29, 2026 14:31
Each of the three subcommand handlers open-coded the same
`--flag value` scanning loop, which is what drove their cyclomatic
complexity (14, 12 and 11 against a limit of 6). They now share a
parseOptions helper returning an Options value, an emit helper for the
repeated markdown/JSON writing, and existingFile for input checks.

Verified all three subcommands produce byte-identical output on the
fixtures, and that --markdown/--json still write their files, a missing
flag value and a non-numeric --fail-if-total-exceeds still produce the
usage error, and a missing input still reports it.

cyclomatic_complexity: 4 -> 1; 58 tests pass; total violations 74 -> 71.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The removed/added counting loop is a separable concern and pushed
appendValueDifferences to complexity 7 against a limit of 6. Also removes
a stray blank line before a closing brace.

Verified the diff engine is unchanged: the 276-line compare report and
the schema-completeness report are byte-identical on the fixtures.

cyclomatic_complexity: 1 -> 0; 58 tests pass; total violations 71 -> 69.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The builder's sub-builders pushed the file to 490 lines and the class body
to 411, both hard errors. They now live in MulticamXMLBuilder+Resources
(assets, formats) and MulticamXMLBuilder+Media (media entries, library),
leaving the entry points and BuildContext in the main file.

BuildContext and the sub-builders become internal, since `private` is
file-scoped and would not be visible from the extensions.

Verified the generated multicam document is still structurally identical.
58 tests pass.

MulticamXMLBuilder errors 2 -> 0; blocking errors 7 -> 5; total 70 -> 69.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The suite was one 623-line file with a 542-line class body, both hard
errors. Report and round-trip tests move to FCPXMLDiffTests+Reports and
FCPXMLDiffTests+RoundTrip; the shared fixtures and helpers stay in the
base file and become internal so the extensions can reach them.

All 58 tests still run (19 in this suite), so nothing was dropped from
discovery by the split.

type_body_length error cleared; blocking errors 5 -> 4.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
testPublicAPIConstructsRoundTripsAndMutatesMinimalProject (118 lines) had
its document literal moved into a makeMinimalProject helper, and
testEveryModelTypeDeclaresNodeEncoding (91 lines) had its three
type/encoding tables hoisted to static constants. Both were hard errors.

58 tests pass; blocking errors 4 -> 2.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Moves the mutation/round-trip tests out of RealFCPXMLTests and the
fixture round-trip tests out of FCPXMLDiffTests into extension files,
clearing the last two file_length errors.

All 58 tests still run. No SwiftLint errors remain.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reverts the global indentation_width/line_length config change in favour
of handling each case at the source.

The 26 XML fixtures in the test suite become real .fcpxml files under
Tests/FCPKitTests/Fixtures, loaded through a fixture(_:) helper. They are
XML documents, so keeping them out of Swift source means their
indentation and line length are no longer measured as Swift. Verified all
26 load byte-identically to the literals they replaced.

The two remaining literals are CLI usage and help text, not XML, so they
cannot become fixture files; they carry a scoped swiftlint:disable with a
comment explaining that the layout is for a terminal. Verified both CLIs
still print with their alignment intact.

58 tests pass; indentation_width: 88 -> 0 with the config reverted;
total violations 171 -> 70.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Extract BuildContext init and split MulticamXMLBuilder media/library builders
- Merge duplicate @main declarations in fcpxml-generator; split main()
- Move XMLTreeInventory, TreeParserDelegate to their own files
- Split FCPXMLDiffCLI main.swift into per-type files
- Extract test helpers/fixtures; document FCPXMLDiff public API

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rplate.

Mixed model types now declare elementKeys and inherit nodeEncoding instead of repeating the attribute/element mapping helper.

Co-authored-by: Cursor <cursoragent@cursor.com>
Swift 6.4 SPM builds no longer produce the index store Periphery expects, so remove the scan and its mise/config wiring until that works again.

Co-authored-by: Cursor <cursoragent@cursor.com>
@leogdion
leogdion merged commit 3e59e49 into v0.1.x Jul 30, 2026
9 checks passed
@leogdion
leogdion deleted the v0.1-scaffold branch July 30, 2026 11:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v0.1.0 FCPKit v0.1.0 milestone work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant