<feed xmlns='http://www.w3.org/2005/Atom'>
<title>domain-dig.git/Docs, branch main</title>
<subtitle>local-first ios domain inspection toolkit. dns, tls, rdap, audit.
</subtitle>
<id>http://git.krz.sh/krz/domain-dig.git/atom?h=main</id>
<link rel='self' href='http://git.krz.sh/krz/domain-dig.git/atom?h=main'/>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/'/>
<updated>2026-08-02T20:35:31+00:00</updated>
<entry>
<title>convert readme to nfo; convert docs to txt; relicense to 0bsd</title>
<updated>2026-08-02T20:35:31+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-08-02T20:29:31+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=fe83ff502ffcaa59c7a22471c634d565972e7644'/>
<id>urn:sha1:fe83ff502ffcaa59c7a22471c634d565972e7644</id>
<content type='text'>
</content>
</entry>
<entry>
<title>update org name</title>
<updated>2026-08-02T14:36:32+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-08-02T06:51:21+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=fa75b888d30e281230e98d58b30d611309431c1d'/>
<id>urn:sha1:fa75b888d30e281230e98d58b30d611309431c1d</id>
<content type='text'>
</content>
</entry>
<entry>
<title>docs: polish repo documentation after the v5.0.0 work</title>
<updated>2026-07-25T16:40:57+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-25T16:39:55+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=b824dae8ea16d20d75136687a7c59bc7b01afe27'/>
<id>urn:sha1:b824dae8ea16d20d75136687a7c59bc7b01afe27</id>
<content type='text'>
Brings the docs in line with the shipped v5.0.0 state and fixes staleness that
accumulated across the release.

- README: add a Tests section documenting the DomainDigTests unit net and that
  the scheme's test action runs both it and the accessibility audit; reword the
  roadmap pointer (v5.0.0 shipped, no longer "planned"); align the contact
  address to hello@zerolabs.sh (was cleberg.net, inconsistent with SECURITY.md).
- ARCHITECTURE: drop the stale "v4.4.1" from the title; note DomainReportExporter
  also renders Markdown and PDF; describe the DomainViewModel concern extensions
  and the ContentView split (SettingsViews / ResultSectionViews); record that
  v5.0.0 stabilized the Local API contract with links to local-api.md and
  data-migration.md; add a Testing section covering both test targets.
- SECURITY: bump the supported-versions table from 4.x to 5.x.

ACCESSIBILITY.md, local-api.md, and data-migration.md were already current and
are unchanged. Docs-only; no code changes.
</content>
</entry>
<entry>
<title>feat: versioned store-migration policy for persisted data (v5 step 2)</title>
<updated>2026-07-25T04:37:00+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-25T04:35:18+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=99e4623af1b08f36120a01b67cbe60df99668651'/>
<id>urn:sha1:99e4623af1b08f36120a01b67cbe60df99668651</id>
<content type='text'>
Third v5.0.0 roadmap item: define and implement a migration policy for the
on-device persisted store (tracked domains, history/snapshots, audits,
workflows, monitoring, settings), so data upgrades cleanly across app versions
instead of relying on a one-shot marker.

- DataMigrationService is reworked from a single boolean marker
  (`data.migrations.v3_4_0`) into a versioned runner keyed by an integer store
  schema version (`data.storeSchemaVersion`). It runs each step once in
  ascending order up to `currentStoreSchemaVersion`, stamping the version as it
  goes. Adding a future migration is now a `case N:` plus a version bump.

  Policy guarantees, all covered by tests:
  - Forward-only and idempotent; every step must be safe on an empty/older store.
  - Never downgrades: a store written by a newer build (higher version) is left
    byte-for-byte untouched.
  - Pre-versioning installs are handled: a set legacy boolean marker reads as
    "already at v1", so the v1 normalization never re-runs for them.

  v1 is the existing normalization pass (dedup + drop the legacy `watchedDomains`
  key + sanitize monitoring settings), now expressed as migration step 1.

- Docs/data-migration.md documents the persisted surface, the two independent
  version lines (store vs. backup export), when to use lenient decoding vs. a
  migration step, the runner contract, an "adding a migration" checklist, and
  backup-import compatibility. Linked from the README.

- DataMigrationServiceTests: 6 tests over legacy fixtures — fresh-store stamping,
  legacy `watchedDomains` migration + key drop, in-place dedup of the stored
  blob, idempotence, legacy-marker-as-v1, and the no-downgrade guard. Full unit
  suite: 58 passing.
</content>
</entry>
<entry>
<title>feat: stabilize and document the Local API v1 response contract (v5 step 3)</title>
<updated>2026-07-25T04:29:01+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-25T04:14:23+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=a52dee116d4066d1b59bd90b4ebc4def4e1597d6'/>
<id>urn:sha1:a52dee116d4066d1b59bd90b4ebc4def4e1597d6</id>
<content type='text'>
Second v5.0.0 roadmap item: make the Local API's public JSON contract explicit,
documented, and regression-locked, so external consumers (Shortcuts, scripts,
integrations) have a stable surface with a defined compatibility promise.

- LocalAPIContract: new single source of truth for the wire-format version
  ("v1") and the canonical JSON encoder (ISO-8601 dates, sorted keys). Both the
  success and error paths in LocalAPIService now route through it, so the format
  can't drift between them, and the ad-hoc per-call-site encoders are gone.

- The response envelope and every payload struct are promoted from `private` to
  internal so the contract is a first-class, testable part of the module. The
  transport/handler internals (request parser, HTTP response, secret store)
  stay private.

- Docs/local-api.md documents the base URL/auth, the envelope, the encoding
  conventions (notably: absent optionals are omitted, not null), every endpoint
  and its payload fields, the error codes, and the semantic-version-style
  compatibility policy (additive changes keep v1; renames/removals/type changes
  bump the version). Linked from the README.

- LocalAPIContractTests: 16 structure/"golden" tests pinning the envelope shape,
  each payload's field names, the enum encodings, and the ISO-8601 date format.
  They assert structure, not values, so ordinary behavior changes don't churn
  them but a renamed or dropped field fails CI. Full unit suite: 52 passing.
</content>
</entry>
<entry>
<title>docs cleanup</title>
<updated>2026-07-24T00:39:46+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-24T00:38:59+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=2ca5ddf53e72379169ab3eb26468a3baa76b0b38'/>
<id>urn:sha1:2ca5ddf53e72379169ab3eb26468a3baa76b0b38</id>
<content type='text'>
</content>
</entry>
<entry>
<title>test(a11y): Phase 6 verification — metadata assertions, middle-band sweep, 27.0 fix</title>
<updated>2026-07-24T00:34:57+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-24T00:20:52+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=7315ae3da604debfacfed9715a04dc91139a31e0'/>
<id>urn:sha1:7315ae3da604debfacfed9715a04dc91139a31e0</id>
<content type='text'>
Executes the Phase-6 manual runbook against the simulator, converting the
mechanically-checkable parts into permanent coverage and reporting the rest
honestly by tier.

- Fix an enforced `.dynamicType` failure surfaced by `audit-a11y.sh current`
  on iOS 27.0: the Settings `Section("Services")` system header (app sets no
  font; 18.6 floor and 26.x CI are clean). Narrow, proven `noiseReason`
  carve-out scoped to dynamicType on the exact Settings header titles. Delta:
  current FAIL -&gt; SUCCEEDED, finding still prints as `[noise: …]`.
- AccessibilityMetadataTests: assert the icon-only control labels and the dense
  Watchlist/Batch row label+value contracts (green on 18.6 and 27.0). These
  were one-time manual VoiceOver checks; now they gate.
- Middle-band Dynamic Type sweep at AccessibilityL across the seeded screens.
  Found no band-exclusive third bug (recorded), retained as regression
  insurance for a band that historically shipped two.
- AccessibilityScreenshotTests: best-effort, non-gating capture utility used to
  produce the cross-runtime Light/Dark/AXXXL screenshots (simctl appearance
  does not propagate headlessly; driven through the in-app picker instead).
- Docs/ACCESSIBILITY_VERIFICATION_RESULTS.md: full pass/fail/not-executable
  matrix + 15 screenshots. Notable positive result: Differentiate Without Color
  IS verifiable via the global com.apple.Accessibility defaults domain.
</content>
</entry>
<entry>
<title>feat(a11y): engage the audit enforcement ratchet (#21)</title>
<updated>2026-07-22T20:04:32+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-22T06:01:05+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=dd41ed2c51beae07288fdfa724e410e0d321d278'/>
<id>urn:sha1:dd41ed2c51beae07288fdfa724e410e0d321d278</id>
<content type='text'>
The point of the Phase 0 harness finally lands: named findings in
textClipped, dynamicType, hitRegion, elementDetection,
sufficientElementDescription, and trait now FAIL the build on the
empty-state suite. Regressions in five phases of accessibility work are
gated, not narrated.

Three carve-outs, each earned by evidence rather than convenience:

- contrast stays report-only. The two long-standing Settings findings are
  rows scrolled under the translucent tab bar, and their attribution
  flips between a row name and nil run-to-run — no suppression is narrow
  enough to keep CI stable. The centralised palette is the real guard.
- The seeded dense-row tests run reportOnly. Bisection showed the audit
  degrades on children-ignored content — the correct VoiceOver treatment
  for dense rows — emitting unattributed contrast/dynamicType failures on
  rows that measure 6-7:1 and render correctly.
- Characterised noise is suppressed narrowly and always logged as
  [noise: reason]: disabled controls (WCAG 1.4.3 exempt), "nearly passed"
  near-misses, system field placeholders (flagged at any length — proven
  by shortening them to no effect), and unattributed clipped/dynamic-type
  artifacts. noiseReason(for:) records each rule's provenance inline.

Validated in both directions. Positive: the full 11-test suite passes
with enforcement live, dark and light, on an erased simulator. Negative:
re-injecting the Phase 3 icon-exposure regression produced two named
[FAIL] findings and a failed suite — on both screens sharing the
component — then went green again on revert.

Docs and the CI workflow comment updated to describe the engaged state.
</content>
</entry>
<entry>
<title>docs(a11y): add Phase 6 manual verification checklist (#21)</title>
<updated>2026-07-22T02:40:58+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-22T02:39:51+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=80cb5d8553d3afd097b10e83cf7af0bf1ba56523'/>
<id>urn:sha1:80cb5d8553d3afd097b10e83cf7af0bf1ba56523</id>
<content type='text'>
A device checklist for everything the automated audit cannot reach:
VoiceOver speech, the More Content rotor, custom-content ordering,
completion announcements, Voice Control label-in-name, the colour/motion/
transparency settings simctl cannot toggle, iPad keyboard focus order,
and both system design languages (classic vs Liquid Glass).

Grounded in the actual implementation rather than generic guidance: each
item quotes the real label/announcement/field it verifies (e.g. "Refresh
all tracked domains", "Lookup complete for &lt;domain&gt;", the More Content
field order) and carries a [Px] tag pointing at the phase that introduced
it. Organised by iOS setting so each is toggled once, with a seed-data
step up front because the dense rows and widget are otherwise untestable
— the same reason they are unverified by the audit today. Includes a
sign-off matrix, a defect-log template, and the known-deferred items so a
ViewThatFits overflow is filed correctly rather than mistaken for a
regression.
</content>
</entry>
<entry>
<title>feat(a11y): color independence, reduce motion, reduce transparency (#21 phase 5)</title>
<updated>2026-07-22T02:40:44+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-22T01:47:34+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=c220e26bb496e455a5bdb6a8dc22abf09ecfed5f'/>
<id>urn:sha1:c220e26bb496e455a5bdb6a8dc22abf09ecfed5f</id>
<content type='text'>
Audit unchanged at 11 dark / 14 light — expected, as none of these
settings are exercised by performAccessibilityAudit, and simctl can
toggle only Increase Contrast, not Differentiate Without Color, Reduce
Motion, or Reduce Transparency. Correct by construction and build-clean;
runtime behaviour is verified in the Phase 6 manual pass.

Color independence:

- Widget status is now an SF Symbol (checkmark.circle.fill /
  exclamationmark.triangle.fill / exclamationmark.octagon.fill), the same
  vocabulary as the in-app badges, replacing a silent colour-only dot on
  both the domain rows and the small-view count pills. Status now survives
  greyscale and reads consistently across surfaces.
- Under accessibilityDifferentiateWithoutColor: the Dashboard
  summary-card dot becomes a per-filter symbol, the selected quick-filter
  chip gains a checkmark and a border (selection was fill-colour only, and
  also gains the .isSelected trait), and LabeledValueRow prefixes a
  warning/failure symbol. All gated on the setting so the default UI stays
  uncluttered.

Reduce motion: all five withAnimation/.animation sites now pass nil under
accessibilityReduceMotion — AppCopyButton's check cross-fade,
CollapsibleSectionView's expand/collapse, TimelineDiffView's scroll, and
WatchlistView's list reorder.

Reduce transparency: the single .thinMaterial capsule falls back to an
opaque AppSurfaceElevated fill under accessibilityReduceTransparency.

The SweepActivityController item from the plan is dropped: it is pure
ActivityKit lifecycle with no animation, confirmed back in the issue
triage.
</content>
</entry>
</feed>
