Zero-friction request evidence: the gem is nobody's nanny (v0.3.1) - #6
Merged
Conversation
Owner escalation on top of the one-switch change. The principle, in their words: the host application and its privacy policy own WHY data is collected; this gem records WHAT was collected, honestly. Its job is evidence mechanics, not gatekeeping collection. So every collection-related purpose, reason, and reference is optional now, at both levels rather than only in the initializer. `record_ip_address`, `record_browser_user_agent`, and `record_ip_geolocation` take zero keyword arguments in a policy. `keep_recorded_*_indefinitely!` takes none at all. `legal_basis_reference:` and the DPIA reference never were required, and a test now pins that they never become so. `record_ip_geolocation` with no field named records the coarse trio — country, region, city — the same set the switch turns on, and nothing finer. The field keywords moved from `false` to `nil` defaults so "did not mention" is distinguishable from "named it and turned it off"; naming every field false is still refused, because calling the verb and disabling everything cannot mean anything, and `do_not_record_ip_geolocation` says that properly. `deliberately_store_request_evidence_unencrypted!` no longer needs a `because:`. The method NAME is the ceremony: `encrypt_recorded_* = false` still cannot be reached without writing that line, and a reviewer still finds it in the diff. Encryption itself is untouched — on by default, all three categories. The install generator stops refusing an incomplete category: enabling a field with no reason and no period writes the file and omits those lines. Scaffolding text is still refused, because a TODO written into a shipped initializer is worse than no line at all and the gem would reject it at boot anyway. Reasons that are not about collection keep their `because:` deliberately — deletion, disposition, legal holds, unredacted export, and the lifecycle verbs. Those record a destructive act, an access, or a state change, where the audit trail IS the reason and there is no honest default for "why did somebody delete this". Rule 6 in CLAUDE.md/AGENTS.md is rewritten around the principle and keeps the parts that are not negotiable: no switch whose name hides what it collects, no overclaiming, no blurring of the receipt states, encryption on by default behind its named call, host-supplied scaffolding still rejected, and a clock alongside keep-indefinitely still refused as a contradiction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013A6ZmfmFuQ2z3GyvQCuECA
v0.3.0 shipped and was tagged from the merge of #5 while this work was in progress, so the escalation is a follow-up release rather than part of the one it was drafted against. Bumps VERSION to 0.3.1 and moves the escalation notes out of the released 0.3.0 entry into their own `## [0.3.1]` section: 0.3.0's entry describes what 0.3.0 did and is left exactly as published. The 0.3.1 entry leads with the principle the owner set — the host's privacy policy owns the why, the gem records the what, and it is nobody's nanny — then lists the zero-keyword record verbs, the coarse-trio default for a bare record_ip_geolocation, the argument-free keep-indefinitely declarations, the optional because: on deliberately_store_request_evidence_unencrypted!, and the installer no longer refusing an incomplete category. Also corrects one doc comment that dated the optional unencrypted `because:` to 0.3.0; it ships in 0.3.1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013A6ZmfmFuQ2z3GyvQCuECA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ships as v0.3.1. Follow-up to #5, on the owner's escalation. v0.3.0 was tagged and released from the #5 merge while this work was in progress, so the escalation is a follow-up release rather than part of the one it was drafted against —
## [0.3.0]in the CHANGELOG is left exactly as published, and this adds a## [0.3.1]entry.#5 made enabling request evidence one switch; this removes the remaining friction everywhere else it survived.
The principle
In the owner's words: the host application and its privacy policy own why data is collected; this gem records what was collected, honestly, and is nobody's nanny. Its job is evidence mechanics, not gatekeeping collection.
#5 removed the boot refusals around the initializer defaults. It left the per-policy surface, the keep-indefinitely declaration, the encryption escape hatch, and the installer still asking for sentences. This finishes the job.
Everything below now works exactly as written
record_verbs.because:,legal_basis_reference:,data_protection_impact_assessment_reference:,delete_after:,retain_until:,encrypted:— all optional in fact, not just in the signature.legal_basis_referenceand the DPIA reference never were required anywhere in the gem; there is now a test pinning that they never become so.record_ip_geolocationwith no field named records the coarse trio (country, region, city) — the same set the switch turns on, nothing finer. The field keywords moved fromfalsetonildefaults so "did not mention" is distinguishable from "named it and turned it off". Naming even one field means you chose the set; naming every fieldfalseis still refused, because calling the verb and disabling everything cannot mean anything (do_not_record_ip_geolocationis how to say that).keep_recorded_*_indefinitely!takes no arguments at all.deliberately_store_request_evidence_unencrypted!no longer needs abecause:. The method name is the ceremony:encrypt_recorded_* = falsestill cannot be reached without that line, and a reviewer still finds it in the diff. Encryption itself is untouched — on by default, all three categories, with a new test pinning that the switch does not weaken it.--record-ip-addresses-by-defaultwith no reason and no period writes the file and omits those two lines. Scaffolding text is still refused (aTODOwritten into a shipped initializer is worse than no line, and the gem would reject it at boot anyway), as is a negative day count.ReviewedTextplaceholder check now only ever applies to text a host actually supplied. Absence is never scaffolding.What deliberately keeps its
because:Reasons that are not about collection:
delete_recorded_ip_address!and siblings,dispose_core_event!,place_on_legal_hold!/release_legal_hold!,plan_disposition_for, unredacted receipt export, and the lifecycle verbs. Those record a destructive act, an access, or a state change — the audit trail is the reason, and there is no honest default for "why did somebody delete this". Flag this if you read the directive more broadly and want them gone too.Also unchanged: the code default is still record-nothing, claim boundaries are untouched, receipt states stay distinct (
not_configured/unavailable/recorded/deleted_after_retention), and no released receipt format changed.Rule 6
Rewritten around the principle, and it names the directive and date so a future agent does not restore the refusals as a "fix". What it keeps as non-negotiable: no switch whose NAME hides what it collects, no overclaiming, no blurring of receipt states, encryption on by default behind its named call, host-supplied scaffolding rejected, and a clock alongside keep-indefinitely still refused as a contradiction.
Tests
bundle exec rake test— 829 runs, 5387 assertions, 0 failures, 0 errors.bundle exec rubocopclean. Coverage 92.46 line / 73.6 branch against floors of 91 / 71.New: every
record_verb with no arguments; a zero-keyword policy capturing end to end and storing exactly the coarse trio; one named field meaning you chose the set; all-fields-false still refused; legal basis never required at either level; the unencrypted ceremony with and without a reason; encryption still on under the switch; and three installer cases replacing the old "incomplete category is refused" pin.🤖 Generated with Claude Code
https://claude.ai/code/session_013A6ZmfmFuQ2z3GyvQCuECA