Skip to content

on-device-privacy: convert "metadata persists, audio doesn't" to a check - #69

Merged
missingbulb merged 1 commit into
mainfrom
claude/prose-to-checks-audio-persistence-d20260729
Jul 29, 2026
Merged

on-device-privacy: convert "metadata persists, audio doesn't" to a check#69
missingbulb merged 1 commit into
mainfrom
claude/prose-to-checks-audio-persistence-d20260729

Conversation

@missingbulb

Copy link
Copy Markdown
Owner

The prose-to-checks-sweep run over .claudinite/local/packs (the only local pack here is on-device-privacy). One RULES.md rule that two prior sweeps (#51, #54) left as prose is now a check.

Tracker: #42

Converted

"No audio is stored at all" → on-device-privacy/no-audio-persistence (blocking)

Both prior sweeps considered this rule and declined it, reasoning that "does this new persisted field carry audio" is a judgment about a field's meaning, not a static signature. That framing is correct for classifying arbitrary new fields — but it isn't the only way to hold the rule. Instead of classifying fields, this check bans the known audio-file-writing APIs directly in mac/Sources/**.swift: AVAudioFile(forWriting:), AVAudioRecorder, ExtAudioFileCreateWithURL, AudioFileCreateWithURL. That's the same absolute-ban shape no-network-client already uses for egress — a fixed list of API tokens, not an attempt to reason about what a hypothetical future field "means."

AVAudioFile(forReading:) is deliberately excluded from the ban — loading a bundled sound asset (e.g. a custom chime) isn't persistence, and a fixture pins that specific non-finding so the check can't drift into banning it by accident.

This holds the promise for v1 (docs/DESIGN-AND-TRADEOFFS.md §6: "in v1, not stored at all"). The roadmap's v3 deliberately adds a rolling audio buffer for on-device retraining — landing that is a product decision, so it updates this check (and the mic usage strings) in the same PR, exactly as no-network-client already expects of any future egress decision.

See-it-fail

node --test .claudinite/local/packs/on-device-privacy/pack.test.mjs — 7/7 pass (3 new): fires on AVAudioFile(forWriting:) and AVAudioRecorder, stays quiet on a read-only AVAudioFile(forReading:) load, and stays quiet on the real capture path (AudioHub.swift, VoiceCommand.swift, Store.swift, LaughDetector.swift, AppLog.swift).

run-active-pack-rules.mjs, check_the_world.mjs, and check_the_work.mjs all exit 0 with the new rule registered.

Left as prose

  • One deletable directory — confidently checking this would need tracking which variable a FileManager write targets back to its declaration, not just grepping a token; left as prose per the skill's guidance rather than shipping a check that can't be made confident.
  • No egress at all / no server or listener — already covered by the existing no-network-client check (it bans import Network outright, which already rules out a listener too); converting either as a separate rule would just duplicate it.
  • The disclosure must stay true — judgment about whether prompt text is still accurate, and "changes in the same commit as the behaviour" is a diff-shaped invariant this check style doesn't hold; no artifact to scan.

Prose trimmed

RULES.md's audio-persistence paragraph drops the mechanical claim, keeps the rationale, and points at the check plus the v3 caveat. README.md's enforcement table moves that row off "prose".

Surface

One new rule module, its pack.mjs line, its fixtures, and the trimmed prose (plus the README table). Nothing outside .claudinite/local/packs/on-device-privacy/.

Left open for review, per this task's open-pr ceiling — a new blocking check is exactly the thing that shouldn't self-merge.


Generated by Claude Code

RULES.md's audio-persistence promise was always-testable but only ever
enforced as prose. Two prior sweeps (#51, #54) left it as prose, reasoning
that "does this new field carry audio" is a judgment about meaning. This
sidesteps that framing entirely: instead of classifying fields, it bans the
known audio-file-writing APIs directly (AVAudioFile(forWriting:),
AVAudioRecorder, ExtAudioFileCreateWithURL, AudioFileCreateWithURL) in
mac/Sources/**.swift — the same absolute-ban shape no-network-client already
uses for egress.

AVAudioFile(forReading:) is deliberately excluded (loading a bundled sound
asset isn't persistence); a fixture pins that false alarm.

Refs #42.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013T5fxiwvVP7REPbFNL174j
@missingbulb
missingbulb merged commit 827f08d into main Jul 29, 2026
@missingbulb
missingbulb deleted the claude/prose-to-checks-audio-persistence-d20260729 branch July 29, 2026 13:34
missingbulb added a commit that referenced this pull request Aug 7, 2026
Three lessons from the 2026-07-26..08-02 window, into the laughcounter local
pack as prose.

#74/#75: diagnosing the installTap crash stalled on "which binary is
installed?" — the menu said only "LaughCounter", and several distinct builds
all reported 0.2.1, because the release workflow keys its Release on
v<version> from Info.plist, so a merge that leaves CFBundleShortVersionString
alone refreshes the same Release behind the latest/download link. Durable
part: show version and build from Bundle.main (not a source constant that
could disagree with the DMG), and bump per distinguishable build.

#56: the scheduler ran green nightly while silently skipping baselining ("no
vendored mount (no stamp)") because the vendored loadConfig dropped the
`claudinite` key it had just validated. Durable part: a job whose success and
whose no-op look identical from outside is telling you nothing — read the skip
line; and a bug inside the mechanism that updates itself has to be fixed out
of band.

#34: claudinite-isolation fired on CLAUDE.md's mount path, which carried
nothing a reader could act on. Durable part: before adding an `accept`, delete
the flagged text and see whether anything actionable went with it — an accept
is for a crossing that must exist.

Nothing new from the mac window (#59, #73, #77, #78, #87, #100, #101, #108):
dev/procedures/mac-audio-lifecycle.md already records the engine-per-start
rule, the inputFormat-vs-outputFormat trap, the aggregate churn, the
three-state health reporting, the witnessed-arrival settle rule and the
observation-gap rule in full. #55, #69, #84 and #99 are already carried by
this pack's existing prose and the on-device-privacy checks. #32's
"prove the check is live" is the see-it-fail discipline the canon owns.
Conversation-logs half: the 2026-08-01 logs are the #108 session (fully
covered above) and unattended task runs; no new friction lesson. No
retention_days configured, so no prune.

Refs #113.


Claude-Session: https://claude.ai/code/session_01G52dxZvLCxyZJJnvLrYcQs

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants