Skip to content

ci: take SwiftLint to 2 warnings, and prove the duplicates are minutes old - #446

Merged
JasonYeYuhe merged 1 commit into
mainfrom
swiftlint-zero-the-warnings
Aug 19, 2026
Merged

ci: take SwiftLint to 2 warnings, and prove the duplicates are minutes old#446
JasonYeYuhe merged 1 commit into
mainfrom
swiftlint-zero-the-warnings

Conversation

@JasonYeYuhe

Copy link
Copy Markdown
Collaborator

Two unrelated things landed together because the second blocked the first: the build broke mid-cleanup with 'CollectorOutcome' is ambiguous, which is the duplicate-source failure again.

SwiftLint: 34 → 2

Both survivors are in files cyq's #432 touches, so they're deliberately left for that PR. --strict still waits — but the follow-up is now "fix 2 and flip a flag" rather than "fix 33 across 15 files".

Most were swiftlint --fix. Three needed judgment and did not get fixed:

  • SubscriptionTier's explicit "free"/"pro"/"team" raw values look redundant and are a wire contract — the enum is Codable and those strings are persisted and exchanged with the backend. Dropping them would bind the serialized form to the Swift case name, so a later rename silently changes stored data. Kept, with the reason at the declaration.
  • APIClient's for/if is not converted to for … where because the condition mutates (seen.insert). A where clause reads as a predicate; burying an insert in one hides the side effect from anyone scanning the loop header.
  • storedMasterstoredLocalOnly was safe only after checking it's a local variable with no persisted key derived from it. The key is Keys.localOnlyMode and is untouched.

Also worth knowing: swiftlint:disable:next for_where must sit above the if, not the for — the rule reports at the if. My first placement silently did nothing.

Duplicate sources: 60 more, and now we know when they arrive

They appeared in a tree this guard called clean twice earlier the same day. Timestamps say 07-27..08-03, in batches sharing one second exactly (26 files at 08-03 01:26:48, 22 at 12:19:59, 7 at 07-30 17:13:57) — a bulk copy, not drift.

The timestamps are a lie, and APFS proves it. Inodes are allocated monotonically:

file birth claims inode
CollectorRunner.swift 08-04 250,454,865
FirstRunPresentation.swift (written that day) 08-18 261,235,600
CollectorRunner 2.swift "08-03" 263,212,836

A duplicate claiming to predate a file by two weeks has a higher inode than it. So it was written to this volume afterwards — during the 08-18 session — carrying a birth time copied from its source.

A timestamp-preserving bulk copy into the working tree, running while a session is active.

Two things it is not, tested rather than assumed:

And the copy reaches inside .git: refs/heads/main 2 (which breaks git pull outright, since a space is illegal in a ref name) and logs/refs/heads/v1 2.15-multi-cli — whose " 2" sits before the last dot, treating .15-multi-cli as an extension. That is macOS's own duplicate-naming algorithm; no compiler, SwiftPM or git names a file that way.

Recorded in the guard so the next investigation starts there instead of re-testing iCloud.

Verification

  • All 60 quarantined before deletion. The two flagged CONTENT DIFFERS were inspected first, as the guard instructs: one had zero unique lines; the other had two that assert the opposite of what the current test deliberately asserts. Nothing lost.
  • 2696 tests pass.
  • Guard clean, swift build clean.

🤖 Generated with Claude Code

…s old

Two unrelated things landed together because the second blocked the first: the
build broke mid-cleanup with 'CollectorOutcome is ambiguous', which is the
duplicate-source failure again.

SWIFTLINT: 34 -> 2

Both survivors are in files cyq's #432 touches, so they are deliberately left
for that PR. `--strict` therefore still waits — but the follow-up is now "fix 2
and flip a flag" rather than "fix 33 across 15 files".

Most were `swiftlint --fix`. Three needed judgment and did NOT get fixed:

  * SubscriptionTier's explicit "free"/"pro"/"team" raw values look redundant
    and are a WIRE CONTRACT — the enum is Codable and those strings are
    persisted and exchanged with the backend. Dropping them would bind the
    serialized form to the Swift case name, so a later rename silently changes
    stored data. Kept, with the reason at the declaration.
  * APIClient's `for`/`if` is not converted to `for ... where` because the
    condition MUTATES (`seen.insert`). A `where` clause reads as a predicate;
    burying an insert in one hides the side effect from anyone scanning the
    loop header.
  * `storedMaster` -> `storedLocalOnly` was safe to rename only after checking
    it is a local variable and no persisted key derives from it. The key is
    `Keys.localOnlyMode` and is untouched.

Also: `swiftlint:disable:next for_where` has to sit above the `if`, not the
`for` — the rule reports at the `if`. My first placement silently did nothing.

DUPLICATE SOURCES: 60 more, and now we know when they arrive

They appeared in a tree this guard called CLEAN twice earlier the same day.
Timestamps say 07-27..08-03 in batches sharing one second exactly (26 files at
08-03 01:26:48, 22 at 12:19:59, 7 at 07-30 17:13:57) — a bulk copy, not drift.

The timestamps are a lie and APFS proves it, because inodes are monotonic:

    CollectorRunner.swift        birth 08-04    inode 250,454,865
    FirstRunPresentation.swift   birth 08-18    inode 261,235,600  (written that day)
    CollectorRunner 2.swift      birth "08-03"  inode 263,212,836  <-- HIGHER

A duplicate claiming to predate a file by two weeks has a higher inode than it.
It was written to this volume AFTERWARDS — during the 08-18 session — carrying
a birth time copied from its source. So: a timestamp-preserving bulk copy into
the working tree, running while a session is active.

Not iCloud (no fileprovider xattr on any specimen) and not a git worktree
(absent from all four). The copy reaches inside .git — `refs/heads/main 2`,
which breaks `git pull` outright, and `logs/refs/heads/v1 2.15-multi-cli`, whose
" 2" sits before the LAST DOT. That is macOS's own duplicate-naming algorithm.
Recorded in the guard so the next investigation starts there.

All 60 quarantined before deletion; the two flagged CONTENT DIFFERS were
inspected first, as the guard instructs — one had zero unique lines, the other
had two that assert the OPPOSITE of what the current test deliberately asserts.
Nothing lost.

2696 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 19, 2026 09:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@JasonYeYuhe
JasonYeYuhe merged commit ba086e0 into main Aug 19, 2026
51 checks passed
@JasonYeYuhe
JasonYeYuhe deleted the swiftlint-zero-the-warnings branch August 19, 2026 09:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants