The binary in bin/ is what ships — the plugin, the MCP bundle and the
Codex and Cursor variants all carry it, never the Go source — and
nothing compared the two. A Go edit whose binary was never rebuilt
shipped the old tool with every check green: the args.go parity check
compares copies, the MCP coverage check confirms presence, and the Go
test step reads the source.
New static check, so it runs on `npm test` and on every commit through
the pre-commit hook. It leads with content — a compiled source changed
while the binary's bytes still match HEAD means the rebuild never
happened, which a `touch` on the binary cannot fake — and falls back to
the existing mtime-plus-dirtiness rule for a binary rebuilt once and
then left behind by a further edit. Source staged without the rebuilt
binary is its own failure, because the working tree can look right while
the commit ships the old binary. Deletions count too: a removed file
cannot appear in a directory walk.
It excludes what `go build` excludes — `_test.go`, `testdata/`,
underscore- and dot-prefixed files, other platforms' GOOS suffixes — so
a flagged binary is always one a rebuild fixes, and it exempts library
modules that ship no binary at all. The walk is guarded: a dangling
symlink used to throw out of Phase 1, skipping every later check and
reporting a crash in the same words as a finding.
`git status` now lists untracked files individually, so a brand-new
package directory is visible to this check and to the bundle and Codex
staleness checks, which a collapsed directory entry hid from all three.
Also ignore xclog's build artifacts, which the rebuild instruction left
untracked in the tree.
`testflight-triage` already covers the local `.xccrashpoint` corpus,
including the two Organizer traps, but the triage agent and command
named only two destinations: a single crash file and a Sentry/ASC
aggregate. A corpus of local bundles fell between them and looked
uncovered from the places a reader starts.
Writing a catalog from a script: the layout Xcode expects, measured on a
970-key catalog where a generic JSON writer produced a 26,531-line diff
that buried the actual translation. The two Apple writers order the
top-level keys differently — the editor by `localizedStandardCompare`,
`xcstringstool` by code point — so a sync over an editor-maintained
catalog re-sorts every key.
`xcstringstool` is the headless path in and out of a catalog, through
`xcrun`. It prunes: an entry missing from the `.stringsdata` is marked
stale if it has translations and deleted outright if it has none, exit 0
and no warning, which makes `--skip-marking-strings-stale` the guard
worth knowing before the first run. It re-emits the canonical layout
only when it has something to write.
Key styles: the source string is `localizations[sourceLanguage]` if
present, else the key itself — a tool has to handle both, and
`sourceLanguage` is not always `en`.
Homographs: a comment gives a translator context, it does not split a
key. One English word used for two concepts is one entry with one
translation and merged comments; each meaning needs its own key with the
English in a default value.
Terminology: the simulator runtime ships Apple's own localized bundles,
which are the exact UI strings, alongside hundreds of AppShortcuts
phrase files — more precise than a support page for the form of a
string, though the support pages still win for what a user says aloud.
Vertical bars: items join one only from inside a system-managed
container, so the same `.bottomBar` items in a bare full-screen cover
render as a horizontal capsule while `toolbarVerticalEdge` still reads
an edge. Hosting a `NavigationStack` in a custom full-screen layer costs
two things worth knowing up front — it paints an opaque background
unless the content inside it sets `.containerBackground(.clear, for:
.navigation)`, and it hands that content the window's safe-area insets
back even under an ancestor's `.ignoresSafeArea()`.
In the bar, a system Toggle or Button ignores `.foregroundStyle` and
`.opacity` and a `.contextMenu` on one does nothing, so a control with
on/off/unavailable states needs a custom view opted in with
`.axisBehavior(.verticalPreferred)`, which keeps its styling and its
context menu. `visibilityPriority(.high)` works on those custom items.
`toolbarVerticalEdge` reports the system's preferred edge whether or not
a bar is visible, and is set before any toolbar item exists, so gating a
layout on it is not circular.
Testing: the 27.1 runtime creates only iPhone Duo, poses are Device
Hub-only, and screenshots default to the inner display — black while
the device is closed.
uikit-modernization: iPad refuses `requestGeometryUpdate` under the
windowing model, and the error handler is the only signal.
Taps: document `--tap-style`, the measured matrix of what each style
activates, and the rule that a tap prints a success line whether or not
anything happened — so the effect is what to assert, never the output.
Selectors resolve to an element's accessibility activation point, so a
view whose frame is skewed gets tapped off target and still reports
success. The session hint and the simulator-tester agent were telling
readers to call `axe tap` bare, which is the inert form; both now point
at `xcui tap`, and the agent's long-press example used a flag `axe tap`
does not have.
Rotation: `devicectl device orientation set` is verified on iPhone and
iPad, with screenshot dimensions as the check. It is a silent no-op on
iPhone Duo, and an app's own `requestGeometryUpdate` is refused on iPad
under the windowing model, so a test that needs landscape rotates from
the host.
Capture: `--mask alpha|ignored|black` and what each shows, plus naming a
display on a two-display device — simctl takes a port UUID, devicectl a
uniqueId, and the default capture is the inner display even when it is
closed and dark. `recordVideo --mask alpha` is accepted but renders
black.
Also: with several simulators booted every device verb now needs
`--udid`, and a broken code fence in device-control-ref.
AXe's default tap style sends a simulator tap that SwiftUI controls
ignore while AXe still prints a success line. Measured on Xcode 27.1
with AXe 1.8.0, across iPhone 17 (iOS 27.0) and iPhone Duo (iOS 27.1):
neither a Button, a List row, a Button inside a List, a Menu, a row of
an open Menu nor a TabView tab activated under the default or the
`simulator` style, and `--tap-style physical` activated all six. The
same default left system alerts on screen while `dialog accept`
reported them handled.
`tap` and the `dialog` accept/dismiss taps now supply the physical
style unless the caller picks one, and an AXe older than 1.7.0 — which
has no `--tap-style` — is named as the cause instead of surfacing as an
unknown-flag failure.
With more than one simulator booted and no `--udid`, every device verb
now exits 2 and lists each booted device's UDID, name and runtime,
rather than driving whichever sorted first while reporting success.
`doctor` reports that list, fails its gate in that state, and leaves
`booted_udid` empty, because nothing may be targeted until the caller
chooses.
- ConcentricRectangle guidance for custom surfaces and bottom sheets
- tvOS SwiftData storage no longer promised as durable
- Foreign keys on new columns without a table rebuild
- concurrency-auditor stops flagging UIKit subclasses
- test-failure-analyzer reports non-isolated main-actor calls as build failures
test-failure-analyzer said calling a @MainActor member from a non-isolated
test becomes a runtime data race under -swift-version 5. It is a compile error
in every language mode, so it breaks the build rather than flaking. Say so,
note that construction depends on the mode, and drop a redundant await from
the corrected example. The agent's docs page no longer calls it a data race.
Verified: the call is rejected under Swift 5 (minimal and complete checking)
and Swift 6; the corrected example compiles cleanly and the broken one fails
with the quoted error.
concurrency-auditor Pattern 1 reported every UIViewController and UIView
subclass without @MainActor as CRITICAL. Both classes are NS_SWIFT_UI_ACTOR,
so their subclasses already inherit main-actor isolation. Scope the pattern to
ObservableObject and other UI-state classes, skip it when the target's
SWIFT_DEFAULT_ACTOR_ISOLATION is MainActor, and list UIKit subclasses as a
false positive. The audit command page's example output now matches.
Verified: an unannotated UIViewController/UIView subclass called from a
nonisolated function fails to compile against the iOS 27.1 SDK; a plain
ObservableObject does not.
database-migration offered only an app-enforced relationship or a table
rebuild, and database-schema-auditor told readers a foreign key could never be
added by ALTER. A new column can carry one: ALTER TABLE ... ADD COLUMN ...
REFERENCES, or GRDB's t.add(column:).references(...). With foreign keys on,
SQLite requires that column to default to NULL, so it starts with no orphans.
Document the route and its decision-tree branch, scope the rebuild to
existing or NOT NULL columns, and make the auditor's searches catch GRDB's
t.add(column:) form. The companion page's rules now match the skill.
Verified: sqlite3 3.54.0 (the iOS 27.1 SDK's version) enforces the key,
refuses a non-NULL default, and cascades only to the deleted album's rows;
GRDB 7.11.1 source for add(column:), references, notNull and defaults.
The tvOS section of swiftdata.md said Caches was the one directory the system
may purge, recommended an Application Support store URL for durability, and
called CloudKit sync optional. Every local directory on tvOS can be deleted
while the app isn't running, as the rest of the suite and axiom-swift's tvOS
skill say. Make iCloud the source of truth and the local store a cache the app
can rebuild.
The Corner Concentricity section named only sheets, glass containers, widgets
and custom containerShape as containers. Apple's ConcentricRectangle
documentation also counts the device's screen: a view that extends to the
display's rounded corners resolves concentric to the hardware, so a custom
surface should never look up the screen's corner radius itself.
Add that rule, a custom bottom-sheet recipe matching Apple's Notes Format
sheet (fixed top corners, concentric bottom corners, not zero), and what
concentricCornerRadii (OS 27) is for: radii without drawing, for Canvas,
animations and other custom surfaces. The companion reference page gains a
prompt and an index entry.
Verified: the new block compiles against the iOS 27.1 SDK at iOS 26.0 and
27.1 targets with no diagnostics.
Pattern 5's consuming close() closed the descriptor and then let deinit close
it again. Add discard self, and move the usage into a function, since a
top-level let is a global that can't be consumed.
Pattern 4 said consume only moves ~Copyable values. It ends any local
binding's lifetime; only bitwise-copyable types get the "has no effect"
warning and stay usable.
Verified: close() now runs once (previously deinit's second close failed with
EBADF); compiler probes for non-trivial copyable, trivial, and ~Copyable values.
MPMediaItemArtwork's request handler is imported without @Sendable, so a
closure written inside a @MainActor type inherits main-actor isolation, and
Swift 6 traps at its entry when MediaPlayer calls it off the main thread.
Capturing the image by value did not prevent that. Mark both handlers
@Sendable and correct the explanation: @Sendable removes the trap, and the
value capture keeps the body from reading main-actor state.
Verified: the previous shape stops with SIGTRAP when invoked from a
background thread; the @Sendable form returns normally. The edited blocks
compile against the iOS 27.1 SDK.
Track.album carried deleteRule: .cascade while Album.tracks cascades too, so
deleting one track deleted its album and, through it, every sibling track.
The NoteTag junction example in swiftdata-migration.md cascaded both of its
to-one sides the same way, and never compiled: Note and Tag had no
initializer.
Drop the cascade from the to-one sides (the default .nullify applies),
declare the junction's inverses, and add the missing initializers.
Verified: deleting one of two tracks now leaves 1 album and 1 track (was 0
and 0); removing one tag from a note keeps every note and tag (was all
deleted). The edited blocks compile against the iOS 27.1 SDK.
The Route 2 rebuild copied rows with an inner JOIN, so any track whose
album_name was NULL or matched no album was left out of the copy and then
deleted by DROP TABLE. PRAGMA foreign_key_check reports nothing, because the
rows are simply gone. Use LEFT JOIN so unmatched tracks survive with a NULL
album_id, and say to carry every kept column and compare row counts.
Also note that inside a GRDB registerMigration the raw PRAGMA/BEGIN lines do
not apply: the migrator already owns the transaction and, with the default
foreignKeyChecks: .deferred, runs the migration with foreign keys off and
checks them right before committing.
Verified: the published block keeps 3 of 3 rows in sqlite3; the inner-JOIN
form keeps 1.
Per-beta probe on Xcode 27.1 beta (27A9269, swiftlang-6.4.0.34.1), macOS and iOS:
every documented Swift 6.4 feature still holds, Continuation escaping-resume is
still rejected at SIL (limitation restamped through 27.1), and mapKeyedValues and
stdlib FilePath remain absent. One arrival: withTemporaryAllocation (OutputSpan
form) probes present, documented in the performance skill with a compile-verified
snippet.
The resize auditor's window-request check now says why a value-based gate is
insufficient: supportsMultipleWindows is true on both displays once the scene
manifest opts in — measured on the 27.1 Duo simulator — so it cannot tell you
which display you are on.
Verified: probe tables in the research notes; the new snippet compiles at the
27.0 floor.
The design suite still said the talks left supportsMultipleWindows on Duo
unstated. It now records the measurement: with UIApplicationSupportsMultipleScenes
set, the value is true on the outer display too, so it reports the scene manifest
rather than the display — the new-window affordance needs the system control to
hide itself off the inner display.
Verified: measurement in the research notes; the build regenerates the
distributions and the docs.
Moving the app to the right half reports the same size classes as the left —
compact width / regular height — and toolbarVerticalEdge flips from .leading to
.trailing, so each half's bar sits on its outer edge. The hub and the probe
results now carry both halves.
Verified: hub compiles 15/15; measurement in the research notes.
Measured on the Duo simulator (iOS 27.1, Xcode 27.1 beta): a Split View half
reports compact width / regular height — the same width class as the outer
display — and the bar follows the app's outer edge (toolbarVerticalEdge reads
.leading in the left half). The hub's truth table, its split-view guidance, and
layout.md now carry the measured values instead of "not stated", and the
multitasking bullet notes the half's layout class.
The same run (already reflected in the gate guidance) showed
supportsMultipleWindows tracks the scene-manifest key rather than the pose:
true with it on both displays, false without it, and unchanged across open/close.
Verified: hub compiles 15/15; probe log, no-key control, and both measurements
recorded in the research notes.
The hub said the talks left supportsMultipleWindows on Duo unknown and that iPhone
reports false. Measured on the 27.1 Duo simulator: with UIApplicationSupportsMultipleScenes
set, the value is true on the closed outer display and the open inner display alike, and
it does not change as the device opens or closes; without the key it is false. The
guidance now says the value tracks the manifest, notes that it cannot distinguish the
displays, and points at UIWindowScene.ActivationAction for an affordance that hides
itself where new windows are not available.
The same run confirmed inner portrait is regular/regular with toolbarVerticalEdge nil,
and the outer display is compact/regular with trailing — matching the truth table.
Verified: hub compiles 15/15; probe log and the no-key control recorded in the
research notes.
Apple shipped the iOS 27.1 SDK and it contains every iPhone Duo API the tech talks
announced, so the hub (axiom-swiftui skills/iphone-duo.md) and the camera reference
(axiom-media) present them as compiling code instead of attributed names. Each 27.1
call carries `@available(iOS 27.1, *)` with its pre-27.1 path, and the 27.0-era
"announced, may not exist" framing is gone. The hub also gains the UIKit side of the
camera-capture accessory (`UISceneAccessory.cameraCapture(sceneConfiguration:)` plus
`registerSceneAccessory(_:)`), which the talk-era tables had no spelling for.
Two rules changed rather than merely confirmed. Preview mirroring is overridden only
when a camera's position and direction disagree; the old text had the app take over
mirroring on every camera switch. Apple's new Technology Overview, HIG page, and
camera articles also add the hero-image extension under a vertical bar, pane-local
controls, overlay-arrangement geometry while folded, and the games screen-fill rule,
all folded in here. The Swift 6.4 deferred-feature table is re-scoped to 27.0 and
27.1 after re-probing both entries.
Verified against Xcode 27.1 (27A9269, iOS SDK 24A94403): the 22 Duo probes flip FAIL
to PASS, the hub's 15 Swift blocks compile at the target floor, the Duo simulator
device type creates against the iOS 27.1 runtime, and the toolchain delta is
additive-only and entirely Duo. Verified: probe 22/22, hub compile 15/15, hook tests
144/144, unit tests 524/524, docs build.
StoreKit and App Intents: `.nonRenewing` → `.nonRenewable`, `@unknown default`
over struct-backed enum-likes (seven switches), `Product.PurchaseResult`
qualification, `needsValueError`, `negativePhrases` typing, `@Parameter(default:)`,
and the SK1 deprecation (iOS 18.0) that no file recorded. The IAP auditor's two
grep patterns that cannot match real Swift are replaced, and the promoted-purchase
detector now targets `PurchaseIntent.intents`.
Runtime: the iOS 16+ simulator does register for remote notifications on
Apple-silicon/T2 Macs; a widget's push path is `WidgetPushHandler`, not
`PKPushRegistry`; `ActivityViewContext` has no `relevanceScore`; `ActivityState.pending`
is iOS 26+; the widget frequent-updates floor is 16.2; `BGTaskScheduler.supportedResources`
is a class property and the submission strategy is `.queue`; timer state-machine
violations trap as EXC_BREAKPOINT/SIGTRAP on arm64 (EXC_BAD_INSTRUCTION is the
Intel lowering); the BackgroundTasks console subsystem is `com.apple.backgroundtasks`.
Privacy and data: required-reason codes corrected against Xcode 27.2's `.xcprivacy`
schema (disk space is 85F4.1/E174.1/7D9E.1/B728.1; C56D.1 is the third-party-SDK
UserDefaults wrapper, AC6B.1 the MDM managed-app-configuration reason); Apple-hosted
asset-pack quota is 200 per app; the inaccessible `unifiedMeContact` and the
macOS-only subgroup operations are gone.
Files in `axiom-performance`, `axiom-security`, `axiom-shipping`, `axiom-ai`,
`axiom-watchos` and `docs/` carrying the same claims were corrected in the same pass.
Cursor, Codex and MCP distributions regenerated.
Verified: `npm test` static gate clean; every changed block extracted from its saved
file and compiled at the file's floor.
Seven defects an independent review of the two previous commits found, all in code
written minutes earlier.
The substantive one: `build:manifest`'s cascade was keyed on whether this run
wrote a file, so a body-only edit to any SKILL.md — the most common edit in a
corpus of skill files — skipped it, and the command reported success over a tree
whose Cursor, Codex and MCP outputs were stale. That is the trap the cascade was
added to close, reached through the guard. It now keys on the distributions' actual
inputs: ask.md, which they embed, or a dirty file under skills/, agents/ or
commands/. The same change stops a manifest-only hand-edit from rebuilding all
three for a bundle timestamp.
In `pre-deploy` check 4: a duplicated manifest entry passed all three comparisons —
a Set and a name→description map both collapse it, and the text comparison saw
nothing wrong — while the comment claimed parity with the unit suite that catches
it; a manifest-excluded skill was reported as "no corresponding skill on disk" when
it is on disk and filtered by MANIFEST_EXCLUDED_SKILLS; and one `skills!.length`
kept the non-null assertion this hunk removed one loop below.
Each case measured. Duplicate → "27 entries for 26 distinct skills". Excluded skill
→ names both possible causes. Clean → 26 descriptions matching. Cascade: a clean
tree stands down in 0.3s, a body-only edit rebuilds all three distributions, a
manifest-only edit rebuilds none.
Not addressed: the bundle step still requires pnpm and repeats package.json's
command string; it now fails with a message naming `npm run build:mcp` rather than
a raw ENOENT stack.
Verified: npm test (static clean), npm run test:unit (524/524).
ask.md is embedded in three generated distributions — the Cursor plugin, the Codex
plugin, and the MCP bundle — so regenerating it makes all three stale. Following
`npm run build:manifest` therefore produced a tree that failed `npm run check:ci`
on staleness the command had just created: the same shape of trap as the
version-script entry point this command exists to replace.
It now cascades, as `set-version.js` already does for Cursor and Codex. The MCP
bundle is cascaded here and not in the release path, because the bundle embeds
commands and the release path leaves that to the staleness gate.
Verified end to end: drifting a router's frontmatter description and running the
command leaves `npm run check:ci` green (524/524, leak scan clean); reverting the
description and re-running restores the tree exactly, the bundle diff being its
build timestamp alone.
Verified: npm test (static clean).
Two defects an independent review of the previous two commits found.
`4fec1423` changed `claudeCode.commands!.length` to `claudeCode.skills!.length` in
the commands loop's summary line, so the gate reported the skill count under a
"manifest commands" label — 26 where 17 were checked. The edit was an anchoring
slip: the replacement text carried `skills`, and it was accepted against a line
reading `commands`. Restored, keeping `?? []` so a manifest without a commands
array reports 0 rather than throwing on a non-null assertion.
The drift check was one-directional. It iterated only the committed array, so a
skill present on disk but absent from the manifest — adding a suite and forgetting
to regenerate, the same mistake pointing the other way — passed `npm test` while
the unit suite caught it. It now compares the name sets in both directions, and
flags a manifest entry with no skill behind it as well.
Verified in four states: description drift, a skill missing from the manifest, a
manifest entry with no skill on disk, and clean. Each injecting case is caught and
names the offending entry; the clean case reports 26 descriptions matching.
Verified: npm test (static clean), npm run test:unit (524/524).
`claude-code.json`'s skills array and the `/axiom:ask` built from it are both
generated from router frontmatter, but that generation lived inline in
`set-version.js` — a version script that refuses to run without a version
argument. A one-line description edit therefore had no regeneration path that did
not look like a release action, and the failing check pointed at the version
script. On 2026-09-17 that cost four consecutive red CI runs.
`scripts/manifest.ts` now owns the generation. Both callers share it:
`npm run build:manifest` for a content-only regeneration, and `set-version.js`
for a release, which folds the same write set into its existing atomic pass.
Versions are untouched by the former.
The generator had no test at all, which is the reason moving it looked risky. It
now has five, covering contracts that have actually broken: the agent list must
come from disk rather than the manifest's always-empty `agents` array (it once
shipped "0 autonomous agents" that way), every manifest skill must appear, no
template placeholder may survive rendering, generation must be deterministic, and
a stale committed description must be replaced by the frontmatter's.
Verified byte-identical to the pre-refactor output: the no-op run and a full
regeneration both reproduce the committed files exactly, and `set-version.js`
through the shared function does too. The tests were shown to fail — injecting
the historical agent-list defect fails the guard and nothing else.
CI now calls `npm run check:ci` (unit suite plus content gate), so the local and
CI gates are one definition instead of two that drift.
Verified: npm test (static clean), npm run check:ci (524/524, leak scan clean).
`claude-code.json`'s skills array is generated from each SKILL.md's frontmatter,
but check 4 only verified that every manifest skill had a SKILL.md on disk — never
that the description matched. The one assertion comparing the two texts lived in
scripts/skill-listing.test.ts, which runs under `npm run test:unit` (what CI runs)
but not under `npm test` (what a maintainer runs), and `node scripts/skill-listing.ts`
reports character budgets only and exits 0 on drift.
So editing a router's description — the most ordinary kind of change in this repo —
left the manifest stale, and the `/axiom:ask` generated from it, through a green
local gate and four consecutive red CI runs.
Check 4 now compares the text and names the first drifted skill. Verified in both
directions: it reports 26 manifest descriptions matching, fails with
`[manifest-drift]` naming axiom-media when a description is injected, and passes
again once that is reverted.
Verified: npm test (static validation clean) and npm run test:unit (519/519).
`claude-code.json`'s skills array is generated from SKILL.md frontmatter, so the
router description corrected in 19208d3a left the committed manifest carrying the
old trigger text — and `/axiom:ask`, which is generated from that array, with it.
The unit suite's drift guard caught it; four consecutive Test Suite runs went red
on nothing else.
Regenerated with `scripts/set-version.js` at the current version, so no version
change is involved: the diff is the description, the generated `ask.md`, and the
Cursor mirror.
Verified: npm run test:unit (519/519) and node scripts/leak-scan.ts (0 errors).
Two defects found by reviewing the previous round's own output.
`carplay-navigation-ref.md` quotes the guide's lane-guidance instructions, which
are written against the Objective-C API: "return a symbol style of
CPManeuverDisplayStyleSymbolOnly for the maneuver." The quote is faithful, but
that constant has no Swift spelling — the Swift form is `.symbolOnly` — so a
reader copying it out of a verbatim quote gets a compile error. The quote stays as
the guide wrote it; the Swift spelling is now noted beside it.
`camera-auditor`'s Pattern 2 search list included
`UIDeviceOrientationDidChangeNotification`, which does not exist in Swift at all.
As a text search it matched only legacy Objective-C-style code, so it could never
fire on a modern app — the same dead-detection-pattern class as the interruption
names corrected earlier, and missed by every previous round. The replacement is
checked against a three-file fixture: the old pattern matched only the legacy
file, the new one matches both spellings, and neither matches the control.
Cursor, Codex, inlined-auditor and MCP distributions regenerated.
Verified: npm test (static validation clean).
Nine passages in the CarPlay files were quoted as verbatim from the CarPlay
Developer Guide while carrying June 2026 page citations, but the wording was the
February 2026 revision's. An earlier pass relabelled the citations and re-derived
the page numbers without requoting the text, so anyone checking a quotation
against the cited page would find different words there.
All are requoted against the current 2026-06-08 revision. Two are whole-sentence
replacements rather than word swaps: the June guide's section is headed "Touch
gestures" and never uses the word "multitouch" anywhere, and the maneuver-metadata
guidance is reworded there.
A sweep of the neighbouring lines found eight further instances of the same two
classes — quotations that had elided words, or closed with a period a sentence the
guide continues — in the same three files.
Separately, the note in `camera-capture.md` about raising the quality ceiling said
it turns on optical image stabilization unconditionally; the header makes that
conditional on the active format's `isHighPhotoQualitySupported`.
Cursor, Codex, inlined-auditor and MCP distributions regenerated.
Verified: npm test (static validation clean).
An independent review of the audit's own fixes found defects the fixes had
introduced or left behind, and the hand-written docs pages — which restate many
of the same claims and are not generated from the skills — had not followed the
corrections.
Fixes that were wrong or incomplete:
- The paused-player elapsed fix was correct only where it was measured.
`playerTime(forNodeTime:)` returns nil whenever the player is not playing —
paused, stopped, or never started — and falling back to the lock screen's
dictionary republished the *previous item's* elapsed on a track change. It now
keeps an app-owned value, reset when a new item loads.
- "Other request types, hand-pose among them, still succeed" is false: measured
across eleven Vision requests, only hand-pose and text recognition run.
Barcodes, animal, body pose, human rectangles, saliency and feature print all
fail to build an inference context, and an order-reversed control on a second
device showed the failure is request-specific, not context exhaustion.
- `teardownCommands()` was left defined and called from nowhere. Two
`isEnabled = true` writes contradicted the file's own rule that registering a
target enables a command by default. The Bluetooth option legend that produced
a recording-recipe straddle still omitted that A2DP is output-only routing.
- Two citations pointed at the wrong guide page, one range omitted the page
holding its section's last step, and one quoted a sentence the guide does not
contain.
The docs sweep found fifteen stale claims across the pages mirroring this suite,
including the automatic-passthrough mechanism the audit disproved, a fabricated
`prepare()` timing figure, and a fabricated set of diagnostic percentages.
`photo-library`'s PNG/JPEG/HEIC split is now measured rather than asserted:
`Image.importedContentTypes()` and `exportedContentTypes()` are both
`["public.jpeg", "public.png"]`, with HEIC and HEIF absent.
Cursor, Codex, inlined-auditor and MCP distributions regenerated.
Verified: npm test (static validation clean).
Every claim checked against the iOS 27.2 SDK and Swift 6.4 — camera, photos,
audio, haptics, ShazamKit, MusicKit, Now Playing, CarPlay, DockKit, screen
capture — with blocks extracted verbatim from the files, compiled individually,
and measured where the claim is behavioural. 25 files, ~13.3k lines, ~408 blocks.
Several published examples cannot compile as shown. `SHSignatureGenerator.signature(from:)`
is static-only, and four call sites invoked it on an instance — including the
file's own "RIGHT" example. `AVProVideoStorage.isBusy` does not exist; the busy
surface is `busyReasons`. `AVCapturePhotoOutput.AppleProRAWQuery` appears nowhere
in the SDK, and the exposure sentinels are `AVCaptureDevice` members, so the
leading-dot spelling in argument position never resolved. The deferred-photo-proxy
delegate parameter is optional, or it fails the protocol's requirement. The
`shazam custom-catalog` invocations used flags the shipping binary rejects.
Two measured claims were the opposite of what the suite taught. Mic input is not
"always 44.1 kHz" — it is the hardware format, 48 kHz on the measured device — and
iOS does not deliver bit-perfect USB DAC output by default: a 96 kHz source renders
at 48 kHz unless the session's preferred sample rate is set. Both files contradicted
themselves 100 lines further down, where the correct advice already sat.
Other corrections: AHAP has no `Metadata` key and the framework drops it silently;
`automaticallyPublishesNowPlayingInfo` was misspelled; `SHLibrary.default.items` is
main-actor isolated; a published `deinit` calling a main-actor method could not
compile, and its replacement left the teardown unreachable while the checklist still
demanded it; CarPlay has 11 app categories, not 10 (video apps arrived at iOS 27),
and 7 universal guidelines, not 8 — with 76 Developer Guide citations re-derived
against the June 2026 edition, whose page numbers differ from the February one the
files cited.
The camera auditor's interruption detection matched three notification names that
exist in no AVFoundation spelling, so it reported every file containing
`AVCaptureSession` as missing observers. The auditor is generated, so the fix landed
in `agents/camera-auditor.md`.
Cursor, Codex, inlined-auditor and MCP distributions regenerated.
Verified: npm test (static validation clean).
Every claim checked against the iOS 27.2 SDK and Swift 6.4, with blocks
extracted verbatim from the files, compiled individually, and — where the claim
is behavioural — rendered or run. 543 blocks scanned across 26 files.
The suite's debugging guidance quoted output that cannot appear. `Self._printChanges()`
prints underscore-prefixed physical names whose depth depends on access level
(`__count` for a private `@State`, `_a` for an internal one), ends every line with
a period, and comma-joins causes onto one line. The advertised `MyView: count
changed` never appears, so the line cannot be grepped out of a console — and the
file omitted `@identity changed`, the marker Apple documents for the state-reset
symptom the file exists to diagnose.
Elsewhere the published fix did not fix the problem:
- The animation guidance for displaying a counting integer does not animate.
Measured with both arms in one run: the plain `Text` body evaluated once, at the
final value, while the file's own `Animatable` view evaluated 76 times.
- ScrollView's `.padding()` vs `.safeAreaPadding()` pair was wrong on all three
counts — both produce identical frames on a safe-area device, and only
`.ignoresSafeArea()` reaches the edge.
- A "conditional ChartContent crashes below 27.0" gotcha names a warning that is
emitted at no deployment target and no language mode; its workaround compiles
as a no-op.
- A `@State` deferral claim holds at the audit floor and is false where the file
says it applies: below iOS 17 the private case silently reverts to eager.
APIs that do not exist in any version were removed or corrected: `PreviewTrait.fixed`,
`@TimelineEntryBuilder`, `@ContentStateBuilder`, `toolbarCustomizationBehavior`,
`usesTextKit2`, `writingToolsResultOptions`, `TableAlias` as a protocol. Search is
bottom-aligned by default from iOS 26, not hidden on scroll. `@MainActor` on a
Codable `@Observable` model is the cause of its conformance-isolation failure, not
the concurrency safety the file advertised. And `simctl … booted` silently targets
whichever device is up — on a host with a stray booted simulator it verifies that
device's screen and exits 0.
Auditor procedures were corrected at their sources in `agents/`: thirteen grep
patterns could never match their target, including `.navigationTitle()` in a
UX-flow auditor, which reports clean rather than broken.
Verified: pre-deploy --static clean; inlined auditors, Cursor render, and audit
areas all current; 543 blocks swept before and after with no compile regressions.
Every claim in axiom-data checked against the iOS 27.2 SDK, Swift 6.4, and the
pinned third-party versions (sqlite-data 1.12.0, StructuredQueries 0.39.2,
swift-sharing 2.10.1, GRDB 7.11.1, realm-swift 20.0.5). Every code block was
compiled individually and the behavioural ones were run — on simulators, against
real stores, and through the system sqlite3 whose version and compile options
match the platform's.
The suite's guidance was wrong in ways that cost data rather than time:
- A chunked importer ended with an unscoped `delete(model:where:)` over an
always-true predicate. It did not compile, and adding the missing `try` would
have made it delete the entire store — 2,000 imported rows plus a pre-existing
unrelated row, measured. The compile error was the only protection.
- The migration file denied that SQLite can add a foreign key to an existing
table, forbade the table rebuild that is the documented route, and recommended
a PRAGMA that is inert inside a transaction — which the same file mandated.
Following it left orphan rows that `PRAGMA foreign_key_check` cannot detect,
the state the suite's own auditor rates CRITICAL.
- The storage reference gated must-save data on
`volumeAvailableCapacityForImportantUsage` and refused to write; Apple's own
documentation for that key says to attempt the write regardless.
- The tvOS storage claim denied that the directories exist and that local data
survives. A simulator probe showed a local-only store keeping every row across
relaunch and reboot; nine files carried the absolute and five cited the
corrected file as their authority while contradicting it.
Also corrected: APIs that exist in no version of their framework
(`@Attribute(.indexed)`, `addColumn(ifNotExists:)`, `migrator.hasBeenMigrated`,
statement `.fetch(_:)`, `.filter`, `TableAlias` as a protocol,
`ResultsSectionCollection`); quoted diagnostics the compiler never emits; an
`@Model` initializer rule the suite documented in one file and violated in
eleven; a `perform` removal that introduced a data race; and the expired
Realm Device Sync migration premise, written in the future tense after its
2025-09-30 shutdown.
Auditor procedures were corrected at their sources in `agents/`, since a dead
detection pattern reports clean — `@Model struct` cannot compile, so the pattern
that advertised it could never fire.
Verified: pre-deploy --static clean; 1,004 sub-skill pointers resolve across 344
emitted Codex files; Cursor output matches a deterministic full render.
The Quick Reference row read "nothing local is persistent" — an absolute that a
tvOS 27.2 simulator probe falsifies. A local-only store kept every row across
terminate, relaunch, and a full device reboot, so local data does persist; it is
simply not guaranteed.
Line 136 of the same file already carried the accurate form, leaving the router
contradicting itself. Both now say "no guaranteed-persistent local storage",
which is the phrasing tvos.md uses.
The fix pass corrected tvos.md in eight places — the core principle, the platform matrix,
the anti-rationalization bullets and the storage section all now say that tvOS has no
guaranteed-persistent local storage, and that the directories exist but none can be relied
on once the app is not running. The router was not updated, so axiom-swift contradicted
itself: tvos.md said the Documents directory exists, and SKILL.md said "no Documents
directory" in three places, including the anti-rationalization row a reader is most likely
to act on.
That is the same class the last review pass caught — a claim corrected in one file and
left standing in another — so it is worth noting that the fix pass cannot see it by
construction: each agent is scoped to one file, and the file that disagrees is never in
scope.
Also verified in this pass, against their sources rather than on report: no "no Documents"
claim remains anywhere in the suite; tvos.md's "§3" pointer resolves to its Storage
Constraints section; and the four mixed-era WWDC numbers the suite cites all return 200.
Verified: 519/519 unit tests, pre-deploy --static clean, distributions regenerated.
Third suite of the corpus audit. 7 files, 3,181 lines, 99 code blocks, audited by four
agents plus the three small files by hand. Compiles at the iOS 18 floor with -emit-sil
(never -typecheck alone, which cannot see the ownership checker) and runs on iOS and tvOS
27.2 simulators where the claim was behavioural.
Two findings were invisible to any compiler and both sat on the suite's core promises.
tvos.md taught that tvOS has no Documents directory and that all local storage is Cache.
Verified on a tvOS 27.2 simulator: the directory is returned, it exists, writes succeed,
and the file survives terminate + relaunch. Library/Application Support is returned as a
URL but does not exist until created, so the table's "Exists?" column was inverted for
both rows. The section now teaches the real constraint — no guaranteed-persistent local
storage — and the prose and table agree.
deep-link-debugging.md presents `simctl openurl` as the unattended primitive behind
/axiom:screenshot and the simulator-tester agent. It is not: on a simulator that has not
approved the scheme once, the first call raises a modal confirmation, exits 0, and
delivers nothing, so the follow-up screenshot captures the alert. Reproduced on a virgin
device and on an already-booted one with the app reinstalled; fixed with a verified
first-run pre-approval step.
Also: transferable-ref.md claimed the importing closure cannot await — it is `async
throws` and the block compiles clean, so the whole workaround was unnecessary — and its
"RIGHT" pattern did not compile and, repaired naively, silently dropped the value it
fetched. The queryItems helper offered as the safe alternative to force-unwrapping trapped
on a repeated key. ownership-conventions.md taught that `borrowing` avoids a copy (same
SIL; the default already borrows) and that `consuming` invalidates a copyable value (only
`~Copyable` does), and quoted three diagnostics the 6.4 compiler never emits. tvos.md
taught subclassing AVPlayerViewController and the player gesture recognizer that the WWDC
session it cites says to avoid. swift-modern.md claimed SwiftUI does not re-export UIKit;
its umbrella header does, on both iOS and macOS.
The fix pass was told to compile and run blocks extracted verbatim from the edited file
rather than probes written alongside — and for the first time in this audit the harness
caught its own work: three bugs in the ownership fix pass, a `~Copyable` global consume
twice and a diagnostic that would not have fired.
Verified: 519/519 unit tests, pre-deploy --static clean, distributions regenerated.
The leak scan reads an enumeration — SURFACES plus ROOT_FILES — so any tracked file
outside it is invisible, and an unread file reads as a clean one. Nine were: .gitignore,
.gitattributes, .mise.toml, and .github/** (dependabot plus the three workflows). All
clean today, which is the point: nothing would have caught it if they were not. This is
the second time the enumeration has drifted — Axiom-77q9 fixed the first, 38 files under
axiom-mcp — and both were found by hand rather than by the gate.
.github is now a surface and the three dotfiles are root files, so the scan reads 1985
files instead of 1978. The workflow's paths filter gained the same four entries, since a
change to a file the scan reads must still start the job — the CI-paths test enforces
that pairing and failed until it did.
The durable fix is the assertion rather than the four additions: a new test derives the
tracked file list from git and fails if any file is in neither the scanned set nor
SELF_EXEMPT, naming the file and where to declare it. Verified by deleting the .github
surface and watching it fail with all four paths listed, then restoring. The third
omission now fails in CI instead of waiting to be noticed.
Verified: 518/518 unit tests, 15/15 leak-scan tests, pre-deploy --static clean.
The Test Suite workflow has failed on every push since it was added. Three tests in
scripts/methodology-leak.test.ts read `.claude/rules/skill-development.md` and
`.claude/skills/preflight/skills/behavioral-testing.md` and die with ENOENT in CI,
because `.claude/` is gitignored: those files exist on a maintainer's machine and
nowhere else. Locally the suite is 518/518; in a fresh checkout it was 515/518.
The tests themselves are sound — they guard a real invariant, that behavioral-test
methodology stays out of the file the harness appends to every skill-file Read, so
it cannot reach a GREEN arm and confound a behavioral test. The subjects are just
local dev state, so the precondition is "this checkout has dev state" and the tests
skip when it does not. They still run wherever the state exists, which is the only
place the leak they guard against could actually occur.
The second test deliberately gates on the presence of the RULES file rather than its
own subject: deleting the canonical file while the rules file remains is the
"fixed it by deleting the content" regression it exists to catch, and it must fail
then, not skip. Verified in three checkouts: dev state present 3 pass / 0 skip;
no dev state 3 skip / 0 fail; rules file present with the canonical file deleted
1 fail. Before the change, the first two were 3 fail and the third was 3 fail.
The workflow did its job here — it is the first thing to run this suite outside a
maintainer's machine, and it found a suite that could not pass outside one.
Reviewing the fix pass found one defect it had reintroduced and several claims it
could not have checked.
The confirmation fixes in testing-async.md replaced a non-waiting pattern with
`await api.fetch { ... }`. That compiles against a callback-taking call, but only
by warning `#UnnecessaryEffectMarker` — the `await` is a no-op and returns
immediately, so the confirmation still races the callback. The fix's own comment
claimed the call "must suspend until the callback has run", which is exactly what
it does not do for the API shape the pattern teaches. Both sites now bridge with
`withCheckedContinuation`, the form the sibling file test-failure-analyzer.md was
already using for the same rule — so the two files agree again. Verified: the
continuation form compiles against a callback-only API (exit 0), the `await` form
compiles only with the no-op warning.
Three claims checked against their sources rather than taken on report:
- `/testing/issue/severity-swift.enum` resolved correctly, and the accessibility
path's underscore-to-hyphen change is right.
- WWDC 2023-10269 and 2024-10206 both redirect; their replacements resolve.
- Xcode's own UI-test template puts `@MainActor` on the test method, so the
annotation ui-recording.md gained is Xcode's convention — but the block's
comment attributed it to the recorder, which the audit could not verify. The
comment now says what the annotation is for instead of where it came from.
Also checked and deliberately left: `await bgContext.perform { }` in axiom-data is
a genuine async overload (compiles clean, no no-op warning), so it is not the same
defect. A stray blank line inside the empty `defaultOptions` object was removed.
Verified: 518/518 unit tests, pre-deploy --static clean, distributions regenerated.
Second suite of the corpus audit. 8 files, 161 code blocks, ~447 API claims checked.
Blocks were compiled at the iOS 18 floor with the Developer-tree XCTest/Testing search
paths and, where the claim was behavioural, executed — `swift test` on the host and
`xcodebuild test` on booted iOS 27.2 simulators.
Two systemic misconceptions, each found independently in more than one file.
`confirmation` does not wait. It checks the count when the closure returns, so a
callback that fires after that is a hard failure, not a wait. testing-async.md taught it
as deterministic waiting and mapped XCTest's `wait(for:timeout:)` to it; the fix it
recommended converted a passing-but-vacuous test into a deterministically failing one.
test-failure-analyzer.md's two "✅ CORRECT" fixes failed 4/4 runs, and its Pattern 5
taught `confirmation(expectedCount: 0)` as proof a callback never fires — it is not: a
late confirm() is counted zero times, reported nowhere, and the test stays green.
A test plan's `userInterfaceStyle` does nothing. The app follows the simulator. Two files
published it as the way to test Dark Mode; a probe app printing its own colorScheme
reported the simulator's setting in every variant, and the JSON block would not load at
all until two missing keys were added. The deterministic route is device-level
`xcrun simctl ui <udid> appearance dark`. `XCUIDevice.shared.appearance` works but races
the launch — measured — so it is described as racy rather than recommended.
Also fixed: a published `.xctestplan` that could never load; a menu item that exists
nowhere in Xcode ("Debug → Record UI Automation"); `XCUIApplication.openURL`, which is not
the API; a fix that could not compile (`self is immutable`); `.timeLimit(.seconds(5))`,
which is explicitly unavailable; and `@Test` inside an XCTestCase subclass, which the
macro rejects.
One agent edited the generated copy of an inlined auditor instead of its agent source.
The drift gate caught it and the work was moved; the gate did its job.
Verified: 518/518 unit tests, pre-deploy --static clean, inlined-auditor and Cursor drift
checks current, all three generated distributions regenerated, and both suites recorded in
the out-of-repo ledger (corpus coverage 44 -> 60 files, 0 stale).
Reviewing the fix pass found three things the fixers' own probes did not.
concurrency-profiling.md's fix added `@concurrent func heavyComputation() async -> Int`
with a comment-only body. That is `error: missing return in global function expected to
return 'Int'` — the fix introduced a defect of the same class it was repairing. It now
returns a value.
The two files that qualified the NonisolatedNonsendingByDefault claim qualified it in
opposite directions: the discipline file said "on by default in new Xcode projects", the
router and the auditor said "off by default". Both are true and neither is complete —
Xcode's Base_ProjectSettings.xctemplate sets SWIFT_APPROACHABLE_CONCURRENCY = YES, so new
Xcode projects have it on, while the build setting's own default is NO and SwiftPM does not
enable it. All five sites now say that. The earlier note in this session calling the claim
unverifiable was wrong: it is checkable in the template's TemplateInfo.plist.
isolation-inheritance-diag.md still told the reader to reproduce the crash by driving Core
Data through `context.perform` from a background-spawned task, after the fix deleted the
section explaining that it no longer traps. Removed — a cutover the fix pass missed.
Verified: 518/518 unit tests, pre-deploy --static clean, inlined-auditor and Cursor drift
checks current.