Skip to content

v2.2.0

Choose a tag to compare

@caglarbaranbora caglarbaranbora released this 22 Aug 22:33
· 15 commits to main since this release
78cfb6f

Added

  • Level 4 Review #3 — L4.5 (does the cited Apple page actually say the quoted rule) closed for all 17 of Tier 2's domains and all 78 Contracts, 14 real defects found and fixed. Reviews #1/#2 explicitly left L4.5 open — a repository-only reading review cannot check live citation content. This one fetches it, via a JSON-endpoint workaround for developer.apple.com/documentation/*'s client-rendered pages, and closes the last four domains (eventkit's final citation, testing, security, backgroundtasks, and app-intents's remaining three Contracts) that a first pass had left open.
    • Two fabricated-but-plausible quotes each in usernotifications/notification-delegate-handling (delegate-timing and actionIdentifier rules) and core-data/managed-object-context-crud (perform/performAndWait and parent-context save rules) — the underlying rule was correct in all four cases, but the quoted sentence did not exist on the cited page. All four replaced with the real text.
    • storekit/transaction-updates-and-restoring-purchases Rule 3 — a misquote reversing Apple's actual claim. Completed a real, truncated quote with fabricated text asserting same-device purchases do replay through Transaction.updates; Apple's page says the opposite — they arrive through Product.PurchaseResult.success(_:) instead, and never through the listener. Fixed.
    • Two wrong rule-number citations in authenticationservices/session-persistence-and-sign-out, neither caught by prior reviews' mechanical id-resolution check (which verifies citation ids, not citation numbers against the target's actual rule count). Fixed.
    • passkit/adding-passes-ui Rule 5 — the review's largest finding, a false scope claim rather than a citation slip. The rule asserted PassKit ships no SwiftUI equivalent for adding a pass to Wallet, requiring UIKit wrapped in a representable. False since iOS 16: AddPassToWalletButton is a SwiftUI-native control that replaces the entire present/delegate/dismiss flow, not just the button. This silently invalidated the exact coupling review #1's T2-01 had verified under the premise (true when written) that no such control existed. Rewrote the rule, ## Intent, ## Scope, and the Compliant Example; corrected the same stale claim in domain-map.md's PassKit entry and in skills/passkit/SKILL.md's description/triggers/routing.
    • tipkit/tip-options-and-app-configuration Rule 2 substituted NSUbiquitousKeyValueStore (iCloud-synced) for UserDefaults (local) in the tvOS default-datastore quote — a materially different persistence mechanism, not a wording nit. Fixed.
    • One misattributed quote in passkit/adding-passes-ui Rule 4 (PKPaymentButton's text attributed to PKAddPassButton) and one minor stale-citation note in passkit/apple-pay-authorization-and-result-handling Rule 4 — both fixed.
    • A link-freshness defect found as a side effect, not L4.5 itself: privacy/tracking-domains-and-third-party-sdk-signatures cited a URL serving a soft 404 (Apple's real "page not found" template at HTTP 200) — a class check_links.py cannot catch by status code alone. Corrected to the live URL (case/trailing-slash differed).
    • Two fork-reported findings were re-verified and ruled out before any edit — a passkit/apple-pay-payment-request citation and an app-intents CaseDisplayRepresentable citation both checked out as correct on independent direct re-fetch, contradicting the initial report.
    • A currency finding in backgroundtasks: submit(_:) is now deprecated (iOS/iPadOS/tvOS 27.0, visionOS 1.0) in favor of the still-Beta submitTaskRequest(_:completionHandler:) — noted in background-task-registration-and-scheduling rather than rewritten around an unstable replacement.
    • Four more fabricated-quote fixes across testing and app-intents: xctest-case-structure-and-assertions, swift-testing-fundamentals (2 paraphrase tightenings), expectations-for-asynchronous-code, app-entities-and-queries (2 fixes, one a quote-order correction, one sourced from a reorganized App Intents doc page not previously cited), and intent-results-and-widget-hookup (OpensIntent's abstract misattributed from its sibling types' pattern).
    • security came back fully clean — 4/4 Contracts, the only domain checked this review with zero findings of any kind.
    • A genuine tooling limit named rather than guessed around: two HIG-page citations in app-shortcuts-and-siri-phrases are left explicitly unverified — Human Interface Guidelines pages have no working JSON-endpoint equivalent, confirmed by testing the pattern directly.
    • Coverage: all 17 Tier 2 domains now have every citable quote checked at least once, with two exceptions named rather than silently passed — the two HIG quotes above, and reference-only citations across localization/foundation/privacy/authenticationservices/storekit with no attached quotation to verify.
  • Level 4 Review #2 — Tier 2's L4.1/L4.4 checks run unsampled for the first time, plus Skill routing content. Review #1 (2026-08-08) covered L4.2/L4.3 completely by construction but sampled L4.1 and checked L4.4 only mechanically (citation ids resolve, not that the citing sentence is still true). This review reads all 78 Tier 2 Knowledge Contracts in full against both checks, then extends L4.4 to the 17 Tier 2 Skills' ## Routing tables — a Skill-to-Knowledge dependency neither prior review had verified as content-true, only structurally resolvable.
    • L4.1 — 0 findings, all 78 Contracts. Confirms on the full population, not a sample, #1's conclusion that the atomicity heuristic over-flags and Tier 2's Contracts are genuinely atomic.
    • L4.4, Knowledge side — 1 finding, fixed. localization/layout-direction-and-rtl-apis's related edge for "general SF Symbols usage" pointed at sf-symbols/symbol-variants, a Contract scoped only to the .fill/.circle/.square/.slash mechanic — not general usage. The Contract's own ### Excluded list already stated the boundary correctly and generically; only the machine-checked edge narrowed it to the wrong target. Repointed to sf-symbols/symbol-basics, the Contract every other sf-symbols Contract depends on. This is the L4.4 failure mode the checklist names but had not yet produced an instance of: Level 2 proves an id resolves, not that it's the right id.
    • L4.4, Skill-routing side — 0 findings, 17/17 Skills, ~78 routing bullets, ~55 Stop-Condition hand-offs. Every routing bullet still matches its target Contract's current scope; every hand-off still names the domain that actually owns the deferred rule.
    • L4.5 (does the cited Apple page actually say the quoted rule) is named as still open rather than silently skipped — it needs fetching live citation content, a different-shaped task from a reading review.
  • Vertical slice #7 — the Tier 3 pilot's own test, and the one boundary its method could not see. The pilot built core-location and photos against nine boundaries classified in domain-map.md before either domain existed, testing slice #6's rule: a boundary decided in advance does not need a slice to find its gaps; one that emerges from two independently-correct domains does. Until now the pilot's verdict came from the build, not from a task. This slice supplies the task — a photo-journal "Places" feature that picks photos, shows where each was taken, and reacts on returning to a place while the app is not running.
    • The rule holds. Four pre-classified boundaries were exercised and none produced a seam defect. The uikit-interaction correction from the previous phase is load-bearing rather than documentary: a SwiftUI task reaches picker-and-selection-results Rule 2 (use PhotosPicker, do not wrap) and never the opposed limited-library Rule 5, so two rules that contradict each other on purpose produced no ambiguity. The backgroundtasks coupling forecloses the BGAppRefreshTask polling loop under the exact phrasing that invites it. Both domains delegate usage-string wording to app-store-review-guidelines in identical shape, which is the duplicated rule the pre-classification was written to prevent.
    • The finding is at the tenth boundary, which nobody classified. PHAsset.location is declared var location: CLLocation? { get } — Photos hands back a Core Location type under the photo-library grant, with no location authorization involved. Neither domain said so. An agent either stops and reports (the task cannot be completed) or requests requestWhenInUseAuthorization() for data already in hand, prompting the user for something the feature never uses. Fixed here: knowledge.photos.asset-fetching Rule 5 owns it, knowledge.core-location.authorization-and-usage-strings excludes it by name, both Skills carry the split in their Stop Conditions, and the Routing Index gains the keywords so the boundary is resolvable where routing happens.
    • The method has a blind spot exactly the size of a multi-domain phase. The classification pass enumerated each new domain against domains that already existed, so a seam between two domains built in the same phase was never a candidate. domain-map.md's Rules now require new domains to be classified against each other, and review-checklist.md L4.3 gains the question that would have found it — does this domain hand back a type another domain owns? Both prior unowned-coupling defects were two Contracts each deferring half a decision; this one is two Contracts saying nothing at all, which L4.3's existing question cannot reach.
    • The photos Reference now indexes 42 Apple URLs (the previous entry's count of 42 was one early — it indexed 41).
  • Tier 3 pilot, second half — the photos domain, and the boundary the pilot got wrong in a new way. 6 Knowledge Contracts, 1 Reference indexing 42 Apple URLs, 1 Skill, 1 Routing Index row. Scope is library access, reading, and saving: selecting assets with no authorization at all, requesting access and its two Information Property List keys, the limited library, fetching, image requests, and writing through a change block. Out of scope for v1: PHContentEditingInput/Output editing, PHLivePhotoView, AVAsset export, PHAssetResource copying, and camera capture (AVFoundation).
    • The pre-classified uikit-interaction boundary named the right neighbour and the wrong symbol. It recorded photos as the third instance of the defect the Tier 2 review repaired in eventkit and passkit — a Contract telling an agent to wrap a view controller in a UIViewControllerRepresentable without naming knowledge.uikit.swiftui-view-representable Rule 5, which owns the constraint that makes the wrap correct. It is not. PhotosUI ships PhotosPicker, a SwiftUI-native picker with the same configuration surface as PHPickerViewController including the photoLibrary: variant, so picker-and-selection-results Rule 2 forbids the wrapper. The coupling is real on a different symbol: presentLimitedLibraryPicker(from:) takes a UIViewController and has no SwiftUI counterpart, so limited-library Rule 5 requires it and names Rule 5 of the owning Contract. Two rules in one domain now disagree about the same technique deliberately, each naming the call it governs.
    • The pilot's verdict, across both domains. Nine boundaries were classified before either domain existed; eight held. Neither failure was the class the pilot was built to prevent — no boundary was mis-assigned and no rule was duplicated across a seam. Both failures were about expressibility: core-location named a domain that cannot legally appear in a shipped artifact, and photos named a symbol whose framework had already solved the problem. Advance classification settles ownership reliably and verifies expressibility not at all, so an entry is a hypothesis about a seam rather than a specification of it.
    • The scope line in domain-map.md names four topic areas and the domain shipped six Contracts. "Authorization and the limited library" and "asset fetching and image requests" are each two atomic concepts; merging either pair breached the 150-line Knowledge cap before its rules were finished.
    • Every rule is quoted from Apple's documentation fetched at authoring time — including the fact most likely to be inverted from memory: the unlabelled authorizationStatus()/requestAuthorization(_:) "aren't compatible with the limited library and return authorized when the user authorizes your app for limited access only," so an agent using them believes it has a full library it does not have.
    • Ships as Draft/0.1.0; promotion to Approved/1.0.0 follows the Approval Gate in docs/artifact-lifecycle.md, not this pull request.
  • Tier 3 pilot, first half — the core-location domain, built against boundaries classified before it existed. 4 Knowledge Contracts, 1 Reference indexing 29 Apple URLs, 1 Skill, 1 Routing Index row. Scope is location access and delivery: authorization levels and their Information Property List keys, the CLLocationUpdate async sequence and the CLLocationManagerDelegate path, requested versus granted accuracy, and running outside the foreground. Out of scope for v1: CLGeocoder, beacon ranging, CLHeading, CLVisit, and map display.
    • The pilot's hypothesis under test (slice #6): a boundary decided in domain-map.md before either side is written does not need a slice to find its gaps. Five boundaries were pre-classified on 2026-08-08; four held without amendment. The backgroundtasks coupling — pre-classified as coupled, with core-location named as the owner — became background-monitoring-and-launches Rule 4 exactly as written: an agent MUST NOT schedule a BGAppRefreshTask to poll location, because significant-change and CLMonitor condition monitoring relaunch the app themselves, so choosing one removes the need for the other rather than sitting beside it.
    • The fifth boundary was correct and unwritable as stated, which is the pilot's second result. The mapkit entry predicted that naming an unbuilt domain "stays correct until mapkit ships, at which point check_scope_vocabulary fails the build." Wrong check, wrong moment: check_prose_domain_resolves fails immediately on any artifact naming a domain with no artifacts, so both the Skill and the Reference had to describe the boundary without naming it. Recordable in domain-map.md, which is not scanned as an artifact; not recordable in anything that ships. The entry is corrected in place rather than replaced, so the wrong prediction stays legible next to the right one.
    • Every rule is quoted from Apple's own documentation, fetched at authoring time rather than recalled — including the two facts an agent is most likely to invert: that requestAlwaysAuthorization() from .notDetermined yields a Provisional Always whose second prompt "will typically display... when your app isn't running", and that setting allowsBackgroundLocationUpdates without the UIBackgroundModes location value "is a fatal error that terminates the app."
    • Ships as Draft/0.1.0, the state docs/artifact-lifecycle.md defines for new work; promotion to Approved/1.0.0 follows its Approval Gate, not this pull request.

Changed

  • v1 finalization, Phase 1 — rule corpus reconciliation. 19 governance documents rewritten and 2 new specifications added, making the repository's own rules self-consistent for the first time. No Knowledge Contract, Skill, or Reference changed, so no release version bump.
    • One metadata dialect. schemas/metadata.schema.md becomes the single field authority: artifact_type/last_updated throughout, a common base plus per-type extensions, and a four-value status enum. The Review lifecycle state is removed — two of three documents already omitted it, and the pull request is the review.
    • Relationship fields given distinct semantics, which is what made the dependency rules enforceable: depends_on is the binding edge and the only field subject to DAG and direction rules; related is a non-binding cross-reference; routes is a Skill's load instruction.
    • New docs/specifications/reference-spec.md. References previously had no specification at all — the validator checked only a line cap, so an empty reference file passed. Reference line cap raised 80 → 98 to fit the metadata block Phase 2 adds.
    • New docs/specifications/skill-management.md — Skill lifecycle: the creation trigger (topical coherence, not size), identity and flat layout, and the add/split/retire procedures.
    • New entry artifact type for skills/apple-agent-kit/SKILL.md, the plugin entry point, which failed the Skill schema on 12 counts because it is not a domain Skill. Three roles are now distinct: a Skill routes Knowledge, a Workflow composes Skills, an Entry is the plugin entry point.
    • Templates are no longer an architectural layer. Four layers everywhere: References → Knowledge → Skills → Workflows.
    • linking-model.md and routing-model.md rewritten to describe the mechanisms that exist rather than ones never built — three linking conventions (metadata ids, wiki links, relative paths), and three-stage routing in which transitive resolution lives in the Knowledge layer via each Contract's ## Dependencies section.
    • validation-model.md now names what enforces each level. Levels 1-3 become code in Phase 3; Levels 4-5 are semantic and become a review checklist, because a heuristic implementation would produce noise that gets silenced.
    • Corrected size limits: the Skill cap was declared as both 60 and 80 in different documents; it is 80.
  • v1 finalization, Phase 2 — metadata migration. The 232 Knowledge Contracts move to the single dialect (type:artifact_type:, updated:last_updated:), and the 31 References gain the metadata block they have never had. Mechanical; no rule, example, or citation changed.
    • scripts/validate_artifact.py migrated in the same commit as the artifacts, so the repository is never in a state where the validator and the files disagree. Field requirements are now expressed as a common base plus per-type extensions, matching schemas/metadata.schema.md field for field.
    • Reference validation exists for the first time: four required sections and a metadata block. Previously --type reference checked only a line cap, so an empty reference file passed.
    • ## Dependencies is now a required Knowledge section, since transitive resolution runs through it. All 232 Contracts already had one.
    • workflow and entry types added to the validator ahead of their first artifacts, so the specifications and the code stay in step.
    • Verified: 295/295 artifacts pass, 25 tests pass (up from 16).
  • v1 finalization, Phase 3 — Validation Levels 1-3 become code. Until now every architectural rule in the repository was enforced by reading. docs/validation-model.md named enforcement for Level 1 only, and the two repository-wide levels were aspirations. Both are now scripts with tests.
    • New scripts/validate_repo.py — 13 repository-wide checks across Levels 2 and 3: id uniqueness, id/path agreement, domain/directory agreement, resolution of all three metadata edge kinds, wiki links, prose relative paths, ## Used By completeness, orphan detection, Routing Index sync in both directions, depends_on direction rules, DAG verification, routing coverage, and the Workflow-composes-Skills rule. Every finding reports level, rule, artifact, and a remediation, as validation-model.md requires of a validator.
    • scripts/validate_artifact.py completes Level 1 with the three checks the model listed but the script never made: status/artifact_type enum values, semantic-version format, and agreement between artifact_type and the artifact's location. A new --all mode validates every artifact in one pass, taking each type from its own metadata — the only way the entry point is validated as an entry rather than as a Skill.
    • Two link conventions needed real rules rather than heuristics to avoid ~50 false positives. ## Used By completeness is matched by URL, never by directory name, because Reference-to-Knowledge is many-to-many. Prose paths in npx/README.md resolve against the repository root, since that file is a published mirror of the root README; links inside code spans and fenced blocks are format examples and are not links.
    • Found and fixed 6 defects no per-file check could see: two References omitted a cross-domain Contract from ## Used By (security.mdlocal-authentication, widgetkit.mdbackgroundtasks); three Reference domain fields carried a title rather than a domain name, a Phase 2 migration artifact; and skills/apple-agent-kit/SKILL.md carried no metadata at all — it is now a declared entry.
    • One finding is left open by design: knowledge/authentication/accessibility-forms.md declares domain: Accessibility while sitting in authentication. Moving it is entangled with the authentication Skill retirement and belongs to Phase 4.
    • Verified: 296/296 artifacts pass Level 1, 67 tests pass (up from 25).
  • v1 finalization, Phase 4 — the Workflow layer becomes real. workflows/ had been a declared layer with a README and no artifacts since the repository began. It now has three, reached through a Routing Index that matches them ahead of Skills.
    • skills/index.md becomes the Routing Index. A Workflows table sits above the Skills table, and the Resolution Rules are rewritten: match Workflows first, and only when the task genuinely spans more than one of the Skills a Workflow names; otherwise load exactly one Skill. This retires the old rule that forbade multi-Skill tasks outright — a rule that had made every cross-domain task unroutable by design.
    • Three Workflows, deliberately of three different shapes so the specification is not fitted to one example. authentication fans out across five domains with two conditional steps. app-store-submission is sequential and gated, which is what exercises ## Exit Conditions. add-widget composes three hand-offs whose boundaries domain-map.md had already resolved.
    • The authentication domain is retired — the first retirement this repository has performed, and the first exercise of skill-management.md's retirement procedure. Its Skill, its four Knowledge Contracts, and its Reference are removed; knowledge/authentication/, skills/authentication/, and references/apple/authentication.md no longer exist.
    • The retirement is a subsumption, not a deletion of unique content. authentication was built before any real domain existed, and every rule it stated was later stated better elsewhere: its two terminology rules against knowledge.style-guide.sign-in-and-authentication-terminology's seven, each cited to an Apple Style Guide page; its four button-label rules against general-button-labels and ui-action-verbs; its four authentication.md "rules", which named no Apple behavior at all and were architecture statements belonging to knowledge-spec.md. What it uniquely had was routing across five domains, and that is a Workflow's job.
    • Retiring it removed a rule that had become wrong: button-labels.md offered "Log In" as a correct internet-account button label, which the Apple Style Guide reserves for local system sessions. Two Contracts in the same repository contradicted each other on it.
    • Seven inbound edges resolved rather than dropped: networking and authenticationservices Contracts and Skills now point at style-guide and authenticationservices, and three style-guide Contracts drop related: entries to Contracts that no longer exist. references/apple/style-guide.md loses two ## Used By rows.
    • One Tier 1 gap is now on the record instead of being papered over. accessibility-forms.md was not migrated into knowledge/accessibility/: three of its four rules were already owned there in far more depth, and all four named no API, which every accessibility Contract does. The fourth — announcing form validation errors to assistive technologies — has no Contract, and is recorded against accessibility in domain-map.md for content completion rather than carried as a substandard file.
    • Closes the one finding Phase 3 left open. scripts/validate_repo.py now passes with zero findings, and caught the two README links to the retired Skill during this phase — the first time the gate found something before a human did.
    • Verified: 293/293 artifacts pass Level 1, validate_repo.py reports 0 findings, 67 tests pass.
  • v1 finalization, Phase 9 — the first Level 4 review of Tier 2, which was overdue and which the mechanical evidence had argued against. Tier 2's 17 domains were built before the review checklist existed, and Phase 5b's read-through covered only Tier 1's human-interface-guidelines and style-guide. No Tier 2 domain had ever been read against Level 4. The prior assessment that Tier 2 conformed rested on Levels 1-3 passing, on the nine stale scope claims splitting proportionally between tiers, and on 27 of 31 declared boundaries being classified — all mechanical, and Phase 7's entire finding was that the mechanical checks are not the ones that catch semantic defects. Recorded as the first entry under validation/reviews/, the Level 4 counterpart to validation/slices/.
    • Method: two mechanical bounds, then aimed reading — the approach Phase 5b's 196-term check established. Every prose `knowledge.x.y` Rule N citation was checked against the target's actual rule count (0 dangling, repository-wide), and every backtick-quoted identifier inside a ## Rules section was mapped to the domains claiming it: 1,604 distinct symbols, 97 claimed by more than one domain, 88 touching Tier 2. Duplication and coupling both live where two domains state rules about the same symbol, which bounded what had to be read.
    • T2-01, the real defect: two Contracts state a mechanic without naming its owner. eventkit Rule 6 and passkit Rule 5 both instruct an agent to wrap a view controller in a UIViewControllerRepresentable; neither names knowledge.uikit.swiftui-view-representable Rule 5, which owns the constraint that makes the wrap correct — do not wrap the controller's view, because that leaves it outside the containment hierarchy. eventkit's related named only its own domain and passkit's was empty. The Phase 5b pattern recurring in a tier the read-through never covered. photos would have been the third instance: its pilot pre-classification said the mechanic "is not duplicated here", which was right about duplication and silent about the pointer.
    • T2-03, structural: the boundary vocabulary had no value for coupling. Cross-Domain Notes used angle-split and clean handoff. Both answer do these overlap? Neither records does a choice on one side fix a choice on the other? Two boundaries classified as clean handoffs produced defects anyway — widgetkitapp-intents (slice #2) and local-authenticationsecurity (slice #5) — and both classifications were correct about overlap. The vocabulary had no way to be wrong and no way to be complete. A third value, coupled, is now defined and requires naming which side owns the coupling rule. Two of the nine pilot boundaries were re-classified under it, which is the pilot's first result and arrived before any Contract exists.
    • T2-04, structural: a boundary classified against an unbuilt domain, never revisited. widgetkit's entry deferred two seams "to domains not yet built"; both shipped and nobody returned. Slice #2's defect is downstream of that. This is the check_scope_vocabulary defect one layer up — a claim true when written that rotted when a neighbour shipped — except it lives in domain-map.md, which no check reads. Now written as a rule.
    • T2-05: four boundaries never classified at all, all now recorded. app-tracking-transparency had three boundaries, two written down and one not.
    • Passes worth recording, because a review reporting only defects cannot be told apart from one that stopped early. No Skill states a rule — 0 MUST/SHOULD outside Stop Conditions, either tier. Authorization is genuinely not duplicated across five permission-gated domains: each states a different shape, and passkit Rule 5 forbids the false generalization by name ("MUST NOT model PassKit's pass-library access as an EventKit-style permission state machine"), which matters because the pilot adds a sixth and seventh shape. UserDefaults carries rules in five domains, four distinct and the fifth already delegating to security unprompted. An atomicity heuristic was built and rejected: it flagged 131 of 258 Contracts, and validation-model.md's own reasoning — a heuristic this noisy gets silenced — is why L4.1 stays a reading check.
    • Verified: 326/326 Level 1, 18 repository checks, 137 tests, 0 invalid transitions. No Knowledge Contract added or removed; no release version change.
  • v1 finalization, Phase 8 — the last open decision, closed by measuring the class instead of the instance; and Tier 3 opens as a falsifiable pilot. Two decisions were outstanding after Phase 7. Both are settled, and the first was settled against both options that had been on record.
    • F-003-01 adopted neither proposed fix. The finding offered a fourth Workflow or a Skill-to-Skill hand-off, and called the Workflow "the smaller change" — reasoning that assumed the join between human-interface-guidelines and style-guide was narrow. It is not: rules constraining what user-facing text must say are spread across 29 of the 33 HIG Contracts. A Workflow would have loaded style-guide plus up to three HIG Skills for any task touching text, which is the opposite of the context minimization Level 5 exists to measure. One count settled what argument could not.
    • A third option was found and rejected on precedent rather than legality: a Skill routing across domains straight to knowledge.human-interface-guidelines.feedback. The dependency table permits it — skill → knowledge carries no domain constraint — but zero of 33 Skills do it, so it would establish a convention, and a convention that spreads makes domain ownership meaningless at the routing layer.
    • The fix is a reporting Stop Condition, in both directions, and finding it required correcting the original diagnosis. F-003-01 was written under F-004-01's lesson that Stop Conditions are read too late. That lesson does not transfer: there the tiebreak was needed to select the Skill, so arriving after selection made it useless. Here the selection is correct — style-guide really does own wording — and the gap appears mid-task, exactly when Stop Conditions are read. Same instrument, opposite verdict. style-guide now stops and reports when a task turns on what text must communicate; human-interface-guidelines-patterns does the same in reverse. Two paragraphs, no new artifact, no precedent set, no layer-order change — and it forecloses nothing, whereas the Skill-to-Skill option was the only one that could not have been undone.
    • Tier 3 opens as a two-domain pilot whose real subject is a rule, not content. Slice #6 claimed that a boundary classified in domain-map.md before either side is written does not need a slice to find its gaps, while one that emerges from two independently-correct domains does. That claim is worth something only prospectively, so core-location and photos have all nine of their boundaries with built domains classified before a single Contract exists — against privacy, app-store-review-guidelines, human-interface-guidelines, backgroundtasks, uikit-interaction, and swiftui, each following an existing precedent rather than inventing one.
    • One of the nine already earned its place: permission-usage-strings names NSLocationWhenInUseUsageDescription among its own examples — a live claim on a key core-location will own. The precedent set for NSUserTrackingUsageDescription and NSFaceIDUsageDescription settles it (an example is illustrative, not ownership), and writing that down now is the difference between a resolved boundary and a duplicated rule found by a read-through two phases later.
    • The pilot is deliberately falsifiable, which is why it is two domains and not nineteen: if its own slice finds a seam defect anyway, #6's rule is weaker than it looked — a result worth having before nineteen domains are built on it.
    • Verified: 326/326 Level 1, 18 repository checks, 137 tests, 0 invalid transitions. No Knowledge Contract added or removed; no release version change.
  • v1 finalization, Phase 7 — Level 4 gets an instrument, and the defect class that scales with growth gets a check. Phase 6 promoted every artifact to Approved and, in doing so, exposed that the gate it passed through was half-built. docs/validation-model.md opens with "a level with no named enforcement is an aspiration, not a gate", named "review checklist" as what enforces Level 4, and no such checklist existed. Every semantic defect this project has found — eight duplicated rules, a mis-scoped cross-reference, an unowned widget rule — was a Level 4 item found by a phase that happened to look. This phase builds the instrument and runs the two Workflows nobody had tested.
    • New docs/contributing/review-checklist.md, seven checks written from the failures that earned them rather than from first principles, wired into .github/PULL_REQUEST_TEMPLATE.md as checkboxes. Two of the seven name failure modes Level 4's own wording did not: a coupling left unowned between two correct Contracts, and a cross-reference that is false at the far end — Level 2 proves the id resolves, never that the rule there says what the citing sentence claims.
    • Slice #5, workflows/authentication — the repository's highest-risk artifact, never tested. It survived the retirement of the domain it is named after, and Phase 5 bolted app-store-review-guidelines onto its front after finding it could produce a screen App Review rejects. Both hold: the retired domain's three Contracts are all reachable under their new owners, and guideline 4.8 really is decided before any view exists. Neither is provable mechanically — check_no_orphans shows a Contract is reachable from some Skill; only a task shows the right ones are reached in the right order.
    • A coupled Keychain decision split across two Contracts, with neither saying it was coupled. local-authentication's biometric-binding rule offered the accessibility constant as a free choice within a family (e.g. .whenUnlockedThisDeviceOnly); security owns the constants and organizes them by sensitivity, correctly excluding biometry. Apple couples them — its own example passes kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, which "prevents items from being stored if the device has no passcode". An agent made two independently reasonable choices Apple treats as one and landed on the weaker guarantee: a biometry-bound item surviving on a device whose passcode was later removed, with the biometric prompt still appearing so nothing looked wrong. The Contract's own Compliant Example used the weaker constant too.
    • Nine Knowledge Contracts called seven built domains "future", and the check that exists for exactly this had never read a Knowledge Contract. check_scope_vocabulary was written for Skills, then widened to a Reference's ## Purpose when the identical claim appeared one layer away. Both times the fix added one artifact type. That was the wrong shape: the question was never which type states scope but where the claim is false, and it is false anywhere it appears — and the 258 Contracts, which hold the most scope statements because every one has an ### Excluded list, were the largest uncovered surface for that reason alone. This class scales with exactly what the repository is built to do: every domain that ships turns every "future <that domain>" into a lie, silently, in files nobody is editing, which is why it has surfaced once per expansion. The reality half now reads every artifact's whole prose body, fenced code excluded; name not in domains keeps a genuinely unbuilt Tier 3 domain legal to call future.
    • Slice #6, workflows/app-store-submission — PASS, no findings, and run partly to see whether a slice could. The privacy seam is the cleanest cross-domain boundary any slice has examined, and the reason generalizes: domain-map.md classified the manifest and the nutrition label as a handoff rather than an angle-split before either domain was written. The widget and Keychain seams were never classified in advance. A boundary decided ahead of both sides does not need a slice to find its gaps; one that emerges from two independently-correct domains does.
    • CONTRIBUTING.md now names all four validation scripts with their scopes — it had said "run the validation scripts under scripts/" while two of the four run on things a working tree cannot see. domain-map.md gains the rule that a boundary resolved in Cross-Domain Notes must also be carried into skills/index.md, since nothing in the Startup Procedure opens domain-map.md.
    • Verified: 326/326 Level 1, 18 repository checks, 137 tests (up from 130), 0 invalid transitions, every changed citation resolving. No release version change.
  • v1 finalization, Phase 6 — Draft → Approved, and the three defects the promotion had to find first. 325 of 326 artifacts were Draft, which made Approved a word the repository used about exactly one file. They are now Approved 1.0.0. What made this a phase rather than a search-and-replace is that docs/artifact-lifecycle.md's Approval Gate has three conditions, and only one of them was met.
    • Level 5's only evidence was void, not merely old. Vertical slice #1 tested five artifacts — skills/login.md and four knowledge/authentication/ Contracts — every one of which was deleted when Phase 4 retired that domain. Its own findings had recommended adding artifact_type, which Phases 1–2 did. A record validating an architecture that no longer exists cannot support an approval, so #1 is marked superseded (not Archived — the lifecycle explicitly places validation reports outside itself, and a document the lifecycle excludes cannot be moved through it) and three new slices were run.
    • Slice #2 — the Workflow layer's first exercise, and an unowned rule. Phase 4 built Workflows, the Entry, and the Routing Index; no slice had ever run against any of them. "Add a widget that lets the user check a task off" routed correctly through workflows/add-widget, loaded 6 Contracts of 326, and correctly skipped backgroundtasks because step 1 decided step 3 was unnecessary. But the rule for what happens after the interaction exists in neither domain: widgetkit defers authoring the intent to app-intents, app-intents defers the widget wiring back, and both are right. The agent then lands on timeline-reloading Rule 1 — written for the general case — and calls WidgetCenter.reloadTimelines from inside perform(). Apple documents the opposite: "When you return from the perform() function, the system reloads the widget's timeline," and "Interactions with a toggle or button always guarantee a timeline reload." The manual call is redundant and spends the 40–70/day budget the same Contract's Rule 2 describes. Apple also states an ordering requirement no Contract carried — work the reload depends on must finish before perform() returns — without which the widget renders the state the person just changed away from. Both rules are now placed: the carve-out and a new Rule 5 on timeline-reloading-and-refresh-budget, because Rule 1 is the rule that was wrong; the ordering requirement on intent-results-and-widget-hookup Rule 5, because the perform() body is its territory.
    • Slice #4 — nine keywords routed to two Skills each. AGENTS.md tells an agent to "select exactly one Skill". ObservableObject, @Published, in-app purchase, IAP, restore purchases, privacy manifest, PrivacyInfo.xcprivacy, RTL, and SF Symbols each appeared in two rows of the Routing Index. Not one was an ownership error — every pair is an angle-split domain-map.md states explicitly, and in several cases the losing Skill's Stop Conditions name the winner outright. That sentence is the tiebreak, and it is unreachable at the moment it is needed: Stop Conditions are read after a Skill is chosen. The keywords are now qualified per row, and check_routing_keywords_unambiguous keeps them that way.
    • Slice #3 — recorded, not fixed. "Fix this error message" routes to style-guide, which owns wording, and never reaches human-interface-guidelines' feedback Rule 6, which owns what a message must say. This is Phase 5b's kind-partition seen from the routing side, and the Index has no way to express a two-domain join — its Workflows table holds three rows, all multi-framework build tasks. Fixing it means either a fourth Workflow or weakening the layer-order rule that a Skill never routes to a Skill; that is a design decision, not a defect fix, and belongs in its own pull request.
    • The lifecycle document demanded four validator rejections and got one. artifact-lifecycle.md requires validators to reject invalid transitions, Approved artifacts with unresolved failures, and edges to Archived artifacts; only the status enum was implemented. Three of the four are now code — Approved requires version >= 1.0.0, no edge may name an Archived artifact, and the prose Status:/Version: header must agree with the metadata block, which nothing had ever compared despite 293 artifacts carrying both copies. The fourth cannot be a repository check at all: a transition is a relationship between two versions of a file, and Levels 1-3 are defined as offline and deterministic. scripts/check_transitions.py runs it on a pull request instead, on the same reasoning that keeps the link checker outside the levels.
    • Verified: 326/326 Level 1, 18 repository checks (up from 16), 130 tests (up from 108), 326 transitions all valid, every changed citation resolving. No rule's meaning changed for any of the 325 promotions, so no Skill routing, Reference, or release version changed.
  • v1 finalization, Phase 5b — the removal read-through, which removed nothing. All 58 Knowledge Contracts of human-interface-guidelines (33) and style-guide (25) were read in full, roughly 200 rules, to decide which duplicate each other and should be retired. The answer is none. Every Contract is atomic and separately routable, and the two domains are partitioned by kind rather than by topic: human-interface-guidelines decides what to build, style-guide decides what to call it. Where they name the same thing they are answering different questions, so the apparent overlaps are not overlaps.
    • This settles the question PR 6 left open. human-interface-guidelines/touchscreen-gestures and style-guide/touch-gesture-verbs are not duplicates and neither retires: the first governs when a gesture may exist at all (alternate input, feedback, no custom gesture for a standard action), the second governs the verb used to describe one ("touch and hold," never "tap and hold"). Each already excludes the other by name, and both already exclude uikit/swiftui gesture implementation. The three-way boundary is correct as built.
    • What the read-through did find is duplicated rules, in eight places, all in contracts that stay. Mechanically confirmed where possible: the 9 table-driven style-guide Contracts define 196 terms with zero collisions, so the finding is confined to prose rules. Each is now resolved with the delegation pattern the repository had already established elsewhere and applied unevenly — one contract owns the rule, the neighbour points at it and states that it defines none, exactly as branding Rule 3 defers to typography Rule 5 and international-formatting Rule 3 defers to units-of-measure.
    • One was a self-contradiction, not merely a repetition. touch-gesture-verbs's Excluded list defers input controls to input-controls — and then its Rule 18 stated the slider rule anyway, down to the same three replacement verbs as input-controls Rule 4. A contract stating a rule it declares out of scope is the one shape of duplication that will diverge silently, because nothing draws attention to the second copy.
    • One was a delegation pointing somewhere that does not cover the case. sf-symbols Rule 5 deferred to "icons Rule 6" for "the general prohibition on replicating Apple hardware" — but icons is scoped to interface icons and explicitly distinguishes itself from the app icon, so nothing general was there to point at. Apple states this prohibition per surface; icons Rule 6 and app-icons Rule 4 now say so and cross-reference each other, and sf-symbols names both.
    • The rest: alerts Rule 8 restated the alert-versus-action-sheet boundary that action-sheets Rule 1 already owned, from the other side, unlinked; feedback Rule 3 restated alerts Rule 1's restraint rule while its own subject is choosing an interruption level; dark-mode Rule 3 re-authored the 4.5:1 baseline that accessibility Rule 2 owns, and the cross-reference between them ran one way only; inclusion's Excluded list deferred point-of-view rules to writing-inclusively, which does not contain them — they are instructional-voice-and-phrasing Rules 6 and 15; undo-and-redo Rule 5 and touchscreen-gestures Rule 7 both governed the three-finger swipe unlinked; and navigation-controls Rule 6 and input-controls Rule 5 read as contradictory on whether "stepper" is sayable until you notice one names the arrow and the other names the control.
    • A duplicated rule is worse than a missing one, which is why this is worth a pass of its own. Both copies are correct on the day they are written; nothing in Levels 1-3 compares them, because no mechanical check can know that two differently-worded paragraphs are the same rule; and the failure only appears when Apple changes the guidance and one copy is updated. The repository has already seen the deferred version of this — two Contracts contradicting each other on "Log In" — which Phase 4 found only by retiring a domain.
    • 10 Contracts revised, 0 added, 0 removed, all bumped 0.1.0 → 0.2.0. No rule's meaning changed, so no Skill routing, Reference, or citation changed. Verified: 326/326 artifacts pass Level 1, validate_repo.py 16 checks report 0 findings, 108 tests pass, 9 cited URLs re-checked and resolving.
  • v1 finalization, Phase 5 PR 9 — every cited URL is now fetched, not merely indexed. PR 8 made indexing enforceable and left the obvious next question unanswered: 615 of the repository's 739 cited URLs had never been fetched by anyone, and no check fetched URLs. scripts/check_links.py closes that. Its first full run found 4 defects across 4 domains, every one of which had passed all three mechanical levels — two 404s and two redirects.
    • check_reference_indexes_citations proves a URL is indexed; nothing proved it resolves, and the two are independent. A URL can be indexed by the right Reference, listed under the right ## Used By, and still be a 404 — which is exactly what coredata/nsmanagedobjectcontext/fetch(_:) was, cited by a Contract whose own prose quotes the signature of the overload it fails to reach.
    • Both 404s are Apple's overload disambiguation, which is the type-vs-member pattern PRs 5-8 found, one case further on. NSManagedObjectContext.fetch(_:) and performAndWait(_:) each have two overloads, so Apple serves them at hashed paths (fetch(_:)-4xeoz, performandwait(_:)-ypye) and 404s the undisambiguated form outright rather than redirecting it. The sibling members on the same page — perform(_:), delete(_:), save() — are not overloaded and resolve fine, which is why nothing looked wrong. The overload was chosen from the citing Contract's own quoted signature, not guessed.
    • A redirect is a finding, not a pass. xctest/xctskipxctskip-swift.struct is the same courtesy redirect PR 8 generalised; it works today and is the address that 404s later, exactly as the two Core Data URLs already have. developer.apple.com/accessibility//documentation/accessibility is the marketing-path-to-documentation form of the same drift.
    • Deliberately not a seventeenth check in validate_repo.py. Levels 1-3 are offline and deterministic, which is what earns them the right to block a commit; this one asks another organisation's web server, so its answer can change without the repository changing. It is a separate script on a separate schedule: on a pull request it checks only the files that pull request touched, and a weekly sweep checks all 739. --strict (unreachable fails too) is right for the sweep and wrong for a pull request, where the outage would be someone else's.
    • scripts/check_links.py deliberately does not reuse validate_repo.py's URL normalisation. That function strips .,); safely because it strips both sides of a comparison, so the damage cancels; here the string is about to be fetched and ) is load-bearing — Apple's member URLs end in one. An early draft copied it and reported 37 nonexistent 404s. Conversely, percent-encoding and trailing slashes are normalised away before deciding whether a redirect moved a page, or every Swift selector URL would report as having moved.
    • CI was running Level 1 only, on 322 of 326 artifacts. validate_repo.py's sixteen repository-wide checks and the test suite were never in CI at all — they existed only on the machine of whoever remembered to run them, while docs/validation-model.md marked Levels 2-3 blocking. The three hand-rolled per-type find loops also covered knowledge/, skills/ and references/ only, so workflows/ had gone unvalidated since Phase 4 created it: a WORKFLOW.md with an invalid version passed CI, verified by breaking one. All three are now validate_artifact.py . --all. Levels 2-3 and the tests are a second job, not a second step: branch protection matches a required check by its displayed name, so folding them into the existing job and renaming it to suit would have left main permanently blocked against a context that never reports again — which is what happened, and is why the job name is now documented as load-bearing in the workflow itself.
    • Correcting the accessibility redirect exposed a fifth defect underneath it. developer.apple.com/accessibility/ and developer.apple.com/documentation/accessibility were both indexed in the same ## Source block — one marketing path and one documentation path for one page — so resolving the redirect collapsed them into a literal duplicate line. This is the two-spellings-of-one-page defect PR 7 and PR 8 each found, in a third form, and it is the reason PR 8 sorted every ## Source block: sorted, the two spellings are adjacent. Nothing catches it, and nothing offline can: the two strings share no prefix.
    • docs/specifications/reference-spec.md (1.1.0 → 1.2.0) makes the canonical-address requirement normative; docs/validation-model.md (1.1.0 → 1.2.0) documents link freshness as deliberately outside the levels, with the reason it cannot be one.
    • Verified: 326/326 artifacts pass Level 1, validate_repo.py reports 0 findings across 16 checks, 108 tests pass (up from 86), and a full sweep reports 739 ok / 0 redirected / 0 broken. The 22 new tests inject the fetcher, so none of them touches the network.
  • v1 finalization, Phase 5 PR 8 — every citation is indexed, and a sixteenth check keeps it that way. No Knowledge Contract is added. This is the Reference pass deferred out of PRs 2 through 7, plus the check that makes deferring it again impossible. 124 URLs cited by Knowledge Contracts across 15 domains were indexed by no Reference at all; all 124 are now indexed, and check_reference_indexes_citations fails the build if a new one appears.
    • The gap was unenforceable by construction, which is why it kept growing. check_used_by_is_complete walks ## Source URLs and asks which Contracts cite each one, so a URL that no Reference indexes resolves to an empty list and touches no check. Coverage could only ever be re-measured by hand — and the hand-measurements drifted three times: 9 References under PR 3's rule, 17 under PR 5's correction, 15 by the time PR 8 ran. Run against main immediately before this pull request, the new check reports 155 findings (124 URLs, several cited by more than one Contract). After it, zero.
    • Third consecutive pull request in which indexing surfaced a stale Apple URL — and it is PR 6's redirect again. localauthentication/laerror and uikit/uiimage/symbolconfiguration both 301 to a -swift.struct/-swift.class form. knowledge/sf-symbols/symbol-weight-and-scale.md was already citing the suffixed spelling while two sibling Contracts cited the bare one, so one domain carried both spellings of one page with nothing to notice — the same defect PR 7 found across two domains. The generalisation is now safe to state: Apple disambiguates a path whenever a name is both a type and a member, and the bare form is never the stable one. Three Contracts are corrected.
    • used-by-complete needed no repair, which was not the expected result — PR 2's equivalent step found a missing row immediately. Indexing 124 URLs produced zero new ## Used By findings and zero cross-domain edges. The hand-maintained reverse indexes were right; what was missing was any mechanism that could have said so.
    • A fifth defect class, found by reading rather than measuring. Four References described built domains as future or unbuilt: foundation on localization and combine, local-authentication and privacy on security, security on authenticationservices. check_scope_vocabulary already forbids exactly this, and its own docstring credits the discovery to skills/foundation/SKILL.md — but the check read Skills only, so references/apple/foundation.md carried the identical claim one layer away, uncaught. It now reads a Reference's ## Purpose too, and reports all five against pre-PR main. The marker vocabulary stays a Skill rule: Deferred/Excluded are defined by skill-spec.md and a Reference has no ## Stop Conditions to carry them.
    • The size-versus-coherence conflict PR 6 recorded finally had a case, and did not have to be settled. references/apple/localization.md reached 100 lines against its 98-line cap once its 7 missing URLs were indexed. reference-spec.md makes not fitting a reason to split the domain's Skill; skill-management.md S1 says size is not the trigger. Localization's six Contracts are one task family, so a split would have been pure arithmetic. Resolved by deleting the Purpose's opening paragraph, which re-enumerated the six areas ## Primary Topics already lists in more detail. A Reference 2 lines over has a trim available; one 20 lines over does not, and that is the threshold at which the conflict must actually be adjudicated.
    • ## Source blocks are now uniformly sorted. The churn is deliberate: PR 7's two-spellings-of-one-page defect, and this pull request's, are both adjacent lines once sorted.
    • docs/specifications/reference-spec.md (1.0.0 → 1.1.0) states the indexing requirement normatively; docs/validation-model.md (1.0.0 → 1.1.0) records both check changes.
    • Verified: 326/326 artifacts pass Level 1, validate_repo.py reports 0 findings across 16 checks, 86 tests pass. Both new checks were proven non-vacuous by disabling them and watching the matching test fail.
  • v1 finalization, Phase 5 PR 7 — app-store-review-guidelines completion. Eight Knowledge Contracts close every guideline the Skill had listed as Deferred — user-generated-content-moderation (1.2), developer-contact-information (1.5), data-security (1.6), copycat-and-impersonation (4.1), login-services-equivalent-option (4.8), third-party-content-licensing (5.2.1-5.2.3), apple-trademarks-and-product-confusion (5.2.4, 5.2.5) — plus review-prompt-api (5.6.1, 5.6.3), which the gap table had not listed.
    • Closes the broken edge that motivated clause (i). workflow.authentication composed five Skills to build a sign-in screen and none of them knew guideline 4.8, so the Workflow could produce a screen App Review rejects. skill.app-store-review-guidelines.submission is now its first step: 4.8 requires an equivalent login option alongside any third-party or social login used for the primary account, so it decides how many buttons the screen has, and discovering it after submission means rebuilding the screen rather than editing it.
    • The gap table listed sections by number, and a number does not say whether the section contains code. Guideline 5.6.1 states "Use the provided API to prompt users to review your app… and we will disallow custom review prompts", which reaches the two designs teams actually build: a satisfaction gate ("Enjoying the app?" routing happy users to the system prompt) is itself a custom prompt, and a "Rate us" button is what Apple's own API documentation tells developers not to build — "Because this method may not present an alert, don't call requestReview() or requestReview(in:) in response to a button tap or other user action." The sanctioned user-initiated path is a product-page link with action=write-review.
    • The four scope questions the design spec deliberately left open are settled. Guideline 4.4 does not satisfy clause (i) — nothing in widgetkit defers to it — so it stays Tier 3, and the missing hand-off is recorded as the thing that would change the answer. 1.1 (objectionable content) is Excluded, not Deferred: it judges what an app contains and no implementation rule satisfies it, while 1.2's four mechanisms are the buildable half. 4.6 is Excluded because Apple's own text is "Intentionally omitted." 4.5 and 4.7 join Tier 3; 5.6 splits, with 5.6.2/5.6.3/5.6.4 Excluded as developer-conduct rules and 5.6.1 built.
    • Ten Contracts were citing a bare hub. reference-spec.md requires a URL "specific enough to authorize a rule" and rules out "a bare hub that indexes unrelated topics". Every Contract in this domain cited the guidelines landing page — one URL for ten different guidelines. The page carries an id per numbered section, so all 20 Contracts now cite per-guideline anchors and the Reference indexes 26 sources instead of 1. First Reference in the kit whose indexing required changing what the Contracts cite, not only what the Reference lists.
    • Second consecutive pull request in which indexing surfaced a stale URL, and the mechanism is worth stating plainly: an unindexed URL is an unverified URL, because nothing fetches a citation no Reference lists. PR 6 found three redirecting UIKit URLs; this one found bundleresources/privacy_manifest_files, now privacy-manifest-files. Here it had a second effect — references/apple/privacy.md already indexed the hyphenated form, so two domains were citing one Apple page under two spellings and used-by-complete could not see the shared edge. Correcting it made the check fire at once, and both References now cross-list.
    • A third hole in prose-domain-resolves, this one a plain bug. The mention regexes match the trailing noun case-insensitively, but the resolver compared it with noun == "workflow" — so a capitalised "Workflow" failed that test and fell through to the domain table, where authentication is retired. Every prose mention of workflow.authentication in that spelling was reported as a dead hand-off while the live Workflow sat in workflows/authentication/. Surfaced by this pull request's own README sentence, which is the third consecutive pass where prose written in the same PR corrected the check that reads prose. Fixed by normalising the noun, with a regression test that fails without the fix.
    • Verified: 326/326 artifacts pass Level 1, validate_repo.py passes 15 checks, 82 tests pass, all 26 cited URLs resolve. The Reference lands at 98 lines against its 98-line cap and is left there deliberately, as networking was — manufactured slack hides the signal. No release-version bump — content only.
  • v1 finalization, Phase 5 PR 6 — uikit completion, and the domain's Skill splits in two. Eight Knowledge Contracts closing every item the Skill had listed as Deferred: gesture-recognizers, gesture-recognizer-coordination, core-animation-layers, uiview-animation, custom-view-controller-transitions, interactive-transitions, swiftui-view-representable, and swiftui-hosting-controller.
    • The Reference did not fit, and the arithmetic decided the split before any judgment did. uikit's 12 existing Contracts cite 34 distinct Apple URLs while references/apple/uikit.md indexed exactly one. Indexing them lands the file at 99 lines against a 98-line cap, so no version of this pull request fit in one Reference. docs/specifications/reference-spec.md states the remedy directly — "If a domain's sources do not fit, split the domain's Skill and give each Skill its own Reference — never raise this limit" — so skills/uikit-interaction/SKILL.md and references/apple/uikit-interaction.md are new, and the seam is the same static-composition-versus-interaction line swiftui was split along in an earlier pass. uikit now indexes 34 sources in 95 lines; uikit-interaction indexes 37 in 94.
    • reference-spec.md and skill-management.md disagree about what may trigger a split, and uikit happens not to expose it. One makes a size overflow a reason to split a Skill; the other says "Size is not the trigger" and requires topical coherence. A domain whose sources exceed the cap but whose Knowledge is a single task family would satisfy one by violating the other. uikit escapes because its seam is real independently, so both specs point the same way. Recorded in the design spec rather than resolved, because resolving it needs a case that actually exhibits it.
    • Four of the eight document a defect with no failure signal. A UILabel given a tap recognizer never fires it, because isUserInteractionEnabled ships off. A UIViewPropertyAnimator built with an initializer runs nothing until startAnimation() — per Apple, "you must explicitly start your animations" — and an unstarted animator is a live object holding an unrun block, so nothing is logged. A frame animation on a constrained view plays correctly and is then reverted by the next Auto Layout pass. An animator that never calls completeTransition(_:) leaves UIKit mid-presentation, after which the app accepts no further presentations.
    • Two rules correct an asymmetry agents routinely get backwards. Per Apple, gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) "returning true is guaranteed to allow simultaneous recognition; returning false, on the other hand, is not guaranteed to prevent" it — so false cannot enforce exclusivity, and require(toFail:) is the tool that can. And a CAAnimation never writes the model layer, so isRemovedOnCompletion = false with fillMode = .forwards holds the end frame onscreen while hit testing and layout keep using the old geometry: the control draws in its new place and responds in its old one.
    • Closes the UIKit-SwiftUI interop boundary PR 4 assigned to uikit but left unbuilt. swiftui-view-representable carries Apple's warning that SwiftUI "fully controls the layout of the UIKit view's center, bounds, frame, and transform properties" and that setting them "results in undefined behavior"; swiftui-hosting-controller records the three defaults an agent accepts silently — sizingOptions defaults to the empty set, safeAreaRegions to .all, and the SwiftUI environment starts at rootView.
    • Both interop Contracts live in knowledge/uikit/ and cite SwiftUI documentation, so references/apple/uikit-interaction.md indexes three frameworks (QuartzCore, SwiftUI, UIKit). That is what reference-spec.md's many-to-many rule is for. One consequence is new: swiftui-hosting-controller cites addChild(_:), indexed by the foundations Reference, so references/apple/uikit.md lists a Contract routed by the other Skill in its ## Used By — permitted explicitly, and the first place in the kit where that permission is load-bearing.
    • Three pre-existing Apple URLs were stale and only surfaced because this pass indexed them for the first time: nsdiffabledatasourcesnapshot, uicollectionviewdiffabledatasource, and uitableviewdiffabledatasource all now redirect to disambiguated paths. Corrected in the Reference and in the two Contracts that cite them (table-view-diffable, collection-view-diffable), which is why a URL that merely redirects is still a traceability defect — a Reference that indexes one records authority it cannot point at directly.
    • Verified: 318/318 artifacts pass Level 1, validate_repo.py passes 15 checks, 81 tests pass, all 71 cited Apple URLs resolve. No release-version bump — content only.
  • v1 finalization, Phase 5 PR 5 — networking completion. Seven Knowledge Contracts closing every item the Skill had listed as Deferred: completion-handler-apis, data-task-publisher, url-session-delegate, background-transfers, transfer-progress-tracking, authentication-challenges, and server-trust-evaluation. networking now covers all three URLSession API families — async/await, completion-handler, and Combine — plus the delegate-driven surface built on top of them.
    • Four of the seven document a defect with no failure signal, a higher proportion than any prior PR in this phase. A session created with a delegate is strongly retained and, per Apple, "leaks memory until the app terminates" unless explicitly invalidated — while every request succeeds. A task that is never resume()d produces no error, no warning, and no callback; the symptom is a spinner that never stops, which reads as a server fault. An unconditional .useCredential(URLCredential(trust:)) accepts any certificate from any host for the session's lifetime and passes every test. A discarded AnyCancellable cancels the in-flight request without calling the sink's completion closure.
    • Two more rules exist because the trap is conditional on data the server controls. A progress callback's expected byte count is NSURLSessionTransferSizeUnknown (-1) when the response carries no Content-Length, so the usual percentage calculation yields a negative fraction rather than an error — it reproduces only against servers that stream. And dataTaskPublisher declares Failure = URLError, so an HTTP 500 is delivered on the value path; a chain without a status-checking tryMap decodes the error page as a payload.
    • Background transfers are documented as their own lifecycle, not a configuration flag: a fixed session identifier, upload/download tasks only (the transfer runs in a separate process), the handleEventsForBackgroundURLSessionurlSessionDidFinishEvents relaunch handshake, session recreation with the same identifier during launch setup, and the file delivered to didFinishDownloadingTo, which per Apple "will be available until your delegate method returns" — so an asynchronous move races the system's deletion and fails only for large files.
    • Server trust is scoped to Apple's two stated reasons (accepting a credential the system would reject; pinning to reject one it would accept), both gated on checking the challenge type and the host. Apple's ATS rule is recorded verbatim: requirements "cannot be loosened" for a protected domain, "but you can tighten them."
    • references/apple/networking.md indexed: 1 URL → 29, ## Used By 8 rows → 15. Checked for the half-finished-split condition PR 4 found in swiftui and does not have it — the file is 98 lines against a 98-line cap. Recorded at the cap rather than trimmed, so the next Contract added forces the split decision instead of letting the file grow past the point where it is answerable.
    • prose-domain-resolves had two holes; both are now closed. The networking Tier 1 row in docs/architecture/domain-map.md still read "Sign-in UX owned by authentication", a live hand-off to a domain retired in Phase 4. The map was skipped wholesale by the check, but removing that exemption alone would not have caught it: both mention regexes anchor on a trailing "domain"/"skill"/"workflow" noun, and owned by `authentication` has none — and that is the scope vocabulary's own hand-off form, used 57 times across the repository. describes_a_retirement() is now describes_a_former_domain() (history verbs, not just "retired" — design was split by rfcs/0001, and its sentence bounds break on blank lines so a table's "Retired 2026-08-07" cell stops leaking forward), a third regex matches the ownership form directly, and domain-map.md is scanned like every other file. Three new tests.
    • Turning it on surfaced four live defects, two written by the passes that introduced the vocabulary. Two were genuine stale routing: the map's authenticationservices entry, and knowledge/authenticationservices/sign-in-with-apple-request-and-credential.md, which had carried owned by `authentication` in its Excluded section since before the retirement — a Contract routing agents to a domain that no longer exists, the exact defect class PR 0 was written to eliminate and missed because nothing could see this phrasing. Two were marker misuse: PR 3 and PR 4 each wrote owned by `<contract>` where the target is a sibling Contract, not a domain. owned by is for cross-domain hand-offs; intra-domain is "see x". Both files used the correct form on adjacent lines.
    • The design spec's 9-Reference table is corrected. Its selection rule was "indexes ≤2 URLs while citing >2" — the set where used-by-complete is vacuous, not the set with unindexed citations. Re-measured across every domain: 17 References cite at least one URL no Reference indexes, ten of them recorded nowhere (security 12 of 19, localization 7 of 44, privacy 6 of 11, eventkit 4, tipkit 3, testing 2, and four with one each). The mechanism is structural — check_used_by_is_complete walks ## Source URLs, so a URL no Reference indexes resolves to an empty list and touches no check. Indexing coverage is unenforceable by construction until a sixteenth check reads Contract references: from the other direction. That check and the indexing pass are both left out of PR 5 deliberately: rewriting ten domains' References under a networking heading is how a scoped PR stops being reviewable.
    • Two new Cross-Domain Notes: networkingcombine (dataTaskPublisher belongs to the domain that owns HTTP requests; combine keeps publisher/operator semantics) and networkingbackgroundtasks (background transfers vs. background work, already named in both Skills' Stop Conditions).
    • Four existing Contracts had their "out of v1 scope" exclusions rewritten as hand-offs to the new Contracts: async-data-fetching, app-transport-security, url-session-configuration, task-cancellation.
    • Verified: 308/308 artifacts pass Level 1, validate_repo.py passes 15 checks, 81 tests pass.
  • v1 finalization, Phase 5 PR 4 — swiftui legacy migration. Two Knowledge Contracts, knowledge.swiftui.observable-object-migration and knowledge.swiftui.navigation-view-migration, closing the gap domain-map.md recorded as "legacy ObservableObject/NavigationView migration" — and the over-promise underneath it, where the Skill's description named ObservableObject as a trigger while its Stop Conditions refused the task. Resolved by building the Contracts, not by narrowing the trigger: an agent asking about ObservableObject is almost always holding existing code, which is exactly the task the Skill should take.
    • The Observation migration's load-bearing rule is that it has no failure signal. A type that has gained @Observable but is still held by @StateObject and injected with .environmentObject(_:) compiles and updates its views — by design, per Apple: "SwiftUI provides this support so apps can make source code changes incrementally." Nothing warns, so completeness cannot be inferred from a green build; the Contract carries the full seven-row wrapper mapping as the only check.
    • Two semantic changes the migration makes that Apple documents and a mechanical rewrite misses: removing @Published inverts the tracking default (opt-in per property becomes every reachable property, unless marked @ObservationIgnored), and invalidation granularity changes, so a view that relied on being invalidated by a property its body never reads stops updating. The migration is not behavior-preserving.
    • The navigation migration's trap is platform-conditional. A NavigationView that shows two columns on iPad and one on iPhone must become NavigationSplitView; substituting NavigationStack compiles and is indistinguishable from correct on iPhone, and silently drops the iPad layout.
    • One recorded gap, two platform floors. Observation requires iOS 17/iPadOS 17/macOS 14/tvOS 17/watchOS 10; NavigationStack/NavigationSplitView require iOS 16/iPadOS 16/macOS 13/tvOS 16/watchOS 9/visionOS 1. A deployment target can permit one and refuse the other, so they are two tasks, not one — a boundary the gap's single-line phrasing had hidden. This is the second consecutive PR to find a gap whose recorded name concealed a version boundary.
    • references/apple/swiftui.md was not merely unindexed — it was a half-finished split. reference-spec.md requires one Reference per Skill-scoped domain, and swiftui has had two Skills since its Animation/Gestures v1; it had one Reference, indexing a single hub URL against the 56 its Contracts cite. Indexing them made the file 118 lines against a 98-line cap, which is the spec's own trigger to split. New references/apple/swiftui-interaction.md covers the interaction surface; references/apple/swiftui.md narrows to foundations. The two URL sets partition with zero overlap — evidence the Skill boundary was drawn in the right place. Its summary, truncated mid-sentence at "State management,.", is repaired, the same defect PR 3 found in accessibility.
    • Closing this gap assigned a boundary the map had left unowned: domain-map.md called UIKit-SwiftUI interop "not yet assigned" while skills/uikit/SKILL.md had already carried it as Deferred. It is now assigned to uikit, and skills/swiftui/SKILL.md hands off there by name. Nothing detects a map-versus-Skill disagreement of this kind.
    • Verified: 301/301 artifacts pass Level 1, validate_repo.py reports 0 findings across 15 checks, 78 tests pass.
  • v1 finalization, Phase 5 PR 3 — accessibility announcements. One Knowledge Contract, knowledge.accessibility.accessibility-announcements, closing the single Tier 1 gap the Phase 4 authentication retirement put on the record: how a form's validation result reaches an assistive app. Built against the API rather than against the retired Contract's prose, which named none.
    • The Contract's load-bearing rule is that an announcement is a channel you cannot verify. post() returns Void and reports nothing about delivery, an announcement leaves behind no element to navigate back to, and Apple documents two of the three priorities as discardable. So an announcement MUST NOT be the only route to the information.
    • Priorities are mandatory whenever announcements can overlap, because equal-priority announcements cut each other off mid-word. Apple's own WWDC23 demonstration posts three at one priority and narrates the result; the transcribed VoiceOver output is "Open--camera--camera active" — two of the three destroyed, and nothing in the app reported it.
    • Two boundaries the recorded gap had hidden by naming two APIs side by side with no module. AccessibilityNotification is published under Apple's Accessibility framework, not SwiftUI (despite Apple's own example for it being SwiftUI code), and is iOS 17+; UIAccessibility.post(notification:.announcement) is UIKit-only and goes back to iOS 4.
    • Scope held to the recorded gap. A second Contract covering the layout- and screen-change notifications was considered and rejected: full-keyboard-access-and-focus Rule 2 already owns .screenChanged with an element argument and moving VoiceOver focus to a failing field. AccessibilityNotification.LayoutChanged and .PageScrolled are left visibly unowned and marked Deferred in the Skill rather than absorbed to look complete.
    • references/apple/accessibility.md had the same defect PR 2 found in xcode: it indexed a single hub URL while its 12 Contracts cited 33 deep pages, leaving used-by-complete vacuous for the whole domain. It now indexes all 33, and its summary — which had been truncated mid-sentence at "labeling, traits,." — is repaired.
    • The defect is systemic. Measured across all 31 References, 9 index two URLs or fewer while their own Contracts cite more than two: swiftui (1 indexed / 50 cited), uikit (1/34), human-interface-guidelines (1/33), usernotifications (2/26), sf-symbols (1/15), networking (1/13), local-authentication (1/9), app-tracking-transparency (2/5), app-store-review-guidelines (1/3). Four are covered by PRs 4-7 by construction; the other five need a pass of their own, recorded in the design spec rather than folded into this PR.
    • Verified: 298/298 artifacts pass Level 1, validate_repo.py reports 0 findings across 15 checks, 78 tests pass.
  • v1 finalization, Phase 5 PR 0 — hand-offs written in prose are now enforced. Phase 4 resolved seven inbound edges to the retired authentication domain and reported zero findings afterwards. Three mentions survived, because they were prose rather than metadata: a Reference's Purpose, a Contract's ### Excluded hand-off telling agents to consult a domain that no longer exists, and a second Reference's scope paragraph. A hand-off in prose routes an agent exactly as far as one in an edge field; it now gets the same enforcement.
    • New Level 2 check prose-domain-resolves in scripts/validate_repo.py (14 checks, up from 13). Prose naming a domain, skill, or workflow must name one that exists. Known names are derived from disk so they cannot drift, and retired names are read from domain-map.md's own retirement rows.
    • Two discriminators were needed. Code-spanning is required for the general case, because "concurrency-focused domain" and "per-domain skill" are English, not names — a first attempt keyed on hyphenation and reported 7 false positives. Retired names are additionally matched bare, since "see the authentication skill" routes into nowhere just as effectively without backticks.
    • Workflow names are deliberately not counted as domain names. workflow.authentication, shipped in Phase 4, took the name of the domain that phase retired; counting it made the check pass on the exact hand-offs it was written to catch, while all 67 unit tests stayed green. Only running it against the real defects exposed this, and a regression test now covers it.
    • The check found a third stale hand-off that a manual grep had missed: references/apple/authenticationservices.md splits `authentication` and domain across a line break, which no line-oriented search matches.
    • Verified: 293/293 artifacts pass Level 1, validate_repo.py passes with 0 findings, 73 tests pass (up from 67).
  • v1 finalization, Phase 5 PR 1 — scope statements get a vocabulary, and it is enforced. ## Stop Conditions was stating three different facts in one phrase: another domain owns it, this domain will own it later, nobody will ever own it. Seventeen of the repository's Skills used interchangeable wording for all three, and the cost was not stylistic.
    • docs/specifications/skill-spec.md gains a Scope Statements section. Every statement of what a Skill does not cover MUST be marked owned by `<domain>` (a hand-off, and the domain must exist), Deferred (this domain will own it), or Excluded (no domain will). All 17 Skills rewritten to it.
    • New Level 3 check scope-vocabulary (15 checks, up from 14). It bans the ambiguous predecessors outright, and separately refuses to let a Skill call a domain future or unbuilt when that domain exists.
    • Five Skills were suppressing domains that had already shipped. foundation called localization and combine "future" and "(Tier 2, unbuilt)"; accessibility said the same of testing; privacy of security; localization of xcode. An agent routed to any of them was told the answer did not exist while 4-6 Knowledge Contracts holding it sat on disk. This is the inverse of PR 0's defect and strictly worse: pointing at a retired domain fails loudly, while denying a live one silently falls back to general knowledge.
    • A fourth stale authentication hand-off, missed by PR 0. skills/authenticationservices/SKILL.md routes to "the authentication Skill" — capital S, and PR 0's check matched the noun case-sensitively. The check now matches the noun case-insensitively (and only the noun: applying re.I to the whole pattern also loosened the name's character class, which matched IBAction in "IBOutlet/IBAction workflow"). It also learned to tell a retirement being recorded from a retirement being routed to, so workflows/authentication/ and the README's What's New may still name what they replaced.
    • docs/architecture/domain-map.md (1.12.0 → 1.13.0) adopts the same vocabulary and gains a Scope Vocabulary section. This corrects the misclassification that motivated it: uikit's Storyboard/XIB support was listed as outstanding work for three months after skills/uikit/SKILL.md had ruled it out permanently.
    • Records the Phase 5 scope decisions in the map: app-store-review-guidelines 1.3/1.4/1.7 and 5.3/5.4/5.5 move to Deferred, Tier 3 as vertical surface rather than being excluded; sf-symbols effects/Composer, swiftui Previews/custom Layout, xcode CLI/CI/SwiftPM, and the eight human-interface-guidelines topics become Excluded; xcode gains the two inherited hand-offs as Deferred, which is the first time they appear in the Skill that owes them.
    • Verified: 293/293 artifacts pass Level 1, validate_repo.py passes with 0 findings, 78 tests pass (up from 73).
  • v1 finalization, Phase 5 PR 2 — xcode gains test plans, code coverage, and project localization. The first Phase 5 pull request to add content. Four Knowledge Contracts close two hand-offs that had been pointing at xcode since before either receiving Contract existed: testing deferred Test Plans and code coverage here, and localization deferred adding a language and the .xcloc/XLIFF round trip.
    • knowledge.xcode.test-plans — the default-plan choice a scheme depends on (creating a plan does not select it), tag filters as the outer filter with the Included checkboxes inside them, one run per configuration, and the multi-scheme sharing that makes one edit reach several schemes.
    • knowledge.xcode.code-coverage — enabling coverage in a test plan's Configurations tab (per configuration or under Shared Settings), scoping collection to chosen targets, and what a percentage does not attest: per Apple, coverage "do[es] not include skipped tests but … do[es] include tests that run marked with known issues or expected failures."
    • knowledge.xcode.project-localizations — localizations are added at the project level, the language ID is what names the .lproj directory, Base is not removable, and a resource left unchecked in the add-a-language sheet is never offered for translation and never warned about.
    • knowledge.xcode.localization-export-import — the .xcloc round trip. Only Localized Contents returns on import; Notes and Source Contents are one-way context. An export taken with Use Compiler to Extract Swift Strings off succeeds and simply omits every Swift string.
    • The localizationxcode boundary the domain map recorded as unresolved is now resolved (1.13.0 → 1.14.0), and the four Skill and Contract scope statements that had marked this surface Deferred now name the Contracts that own it.
    • references/apple/xcode.md (0.1.0 → 0.2.0) indexes its sources for the first time. It carried a single hub URL while its eight Contracts cited deep pages, so used-by-complete — which matches by URL — had nothing to check. Listing the real URLs put the domain under that check, which immediately found knowledge.xcode.project-localizations missing from references/apple/localization.md's ## Used By.
    • Two CHANGELOG defects fixed in passing: Phase 4's Verified line had been orphaned below the Phase 5 entries inserted above it, where it read as a contradictory second verification of PR 1; and the [2.0.0] entry counted 18 Tier 2 domains, one of which was a Markdown table header row. The true total is 17.
    • Verified: 297/297 artifacts pass Level 1, validate_repo.py passes with 0 findings, 78 tests pass.