Sdlc/Structural/QueryMethodReachesWrite: anchor WRITE_NAME and QUERY_NAME to verb positions #1380
Labels
No labels
P0
P1
P2
P3
bug
category/code-change
category/cross-repo
category/eval-run
category/investigation
category/no-test-target
category/owner-ruling
category/prose-artifact
category/recurring-chore
category/structure
category/umbrella
crc-refactor
create
delete
enhancement
filed-by/agent
filed-by/user
frozen
lint-rule
needs-info
needs-triage
next
plugin/cc-architect
plugin/os
plugin/os-adr
plugin/os-aidd-lint
plugin/os-backlog
plugin/os-context
plugin/os-doc-hygiene
plugin/os-sdlc
plugin/os-vault
project/cc-os
ready-for-agent
ready-for-human
recurring
review
rule-proposal
update
waiting
wayfinder:grilling
wayfinder:map
wayfinder:map
wayfinder:research
wayfinder:task
wayfinder:task
wontfix
worklist/deviations
worklist/lint-rule
worklist/new-implement-build
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jared/cc-os#1380
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
The cop
Sdlc/Structural/QueryMethodReachesWritematches write and query names anywhere in a method name, so pure computations get flagged.Its one hit is a false positive.
Found by the #1325 zero-false-positive audit (docs/plans/class-length-family/sdlc-cop-audit.md).
Observed
Counterexample:
plugins/os-context/audit/bin/extract:237.buildreachesread_write_profile(:256), which only computes a hash. The unanchoredWRITE_NAMEmatches the nounwrite_profileandrewrite.QUERY_NAMEalso matchesformatandforce.Reproduce
The cop matcher is at
plugins/os-sdlc/lib/os_sdlc/cops/structural/query_method_reaches_write.rb(QUERY_NAME,WRITE_NAME); the chain walk is inplugins/os-sdlc/lib/os_sdlc/cops/structural/query_method_reaches_write/write_chain.rb. Fromplugins/os-sdlc, runbundle exec rubocop --only Sdlc/Structural/QueryMethodReachesWrite ../os-context/audit/bin/extract: 1 offense today, false.Approach
Implement the rule in
docs/lint-lessons/analyses/sdlc-structural-query-method-reaches-write.mdstep 6. Carve-outs go in the cop matcher, never in a config Exclude list or a test-path exclusion.\A(for|find|fetch|get|build)(_|\z)and does not end with?or!.\A(save|persist|write|update)(_|!|\z), OR its body sends a method whose name matches that same anchored pattern (for exampleproject.save_tracker,File.write).Required work:
(_|!|\z)inplugins/os-sdlc/lib/os_sdlc/cops/structural/query_method_reaches_write.rbOwner sketch
QueryMethodReachesWrite(cop,structural/query_method_reaches_write.rb) owns the two name patternsQUERY_NAMEandWRITE_NAME.QueryMethodReachesWrite::WriteChain(structural/query_method_reaches_write/write_chain.rb) owns the decision "is this called def a write def": its name matchesWRITE_NAME, or its body sends a method whose name matchesWRITE_NAME.QueryMethodReachesWrite::WriteChainOffensekeeps the message; it already names the concrete write send throughconcrete_write_call.plugins/os-sdlc/tests/cops/query_method_reaches_write_test.rb.Acceptance criteria
Must not fire:
buildthat callsread_write_profile, which only computes a hash, when the cop runs, then it reports no offense.formatthat callswrite_report, when the cop runs, then it reports no offense.force_reloadthat callsupdate_cache, when the cop runs, then it reports no offense.findthat callsrewrite, which only computes a string, when the cop runs, then it reports no offense.Must still fire (near shapes the new rule decides; each is red before the change):
5. Given
find_userthat callsstore_user, whose body sendsFile.write(path, data), when the cop runs, then it reports an offense.6. Given
fetch_configthat callsapply_config, whose body sendsconfig.update!(values), when the cop runs, then it reports an offense.7. The existing test
test_flags_a_query_named_method_whose_same_file_chain_reaches_a_write(self.forcallsresolve_and_persist, which sendsproject.save_tracker) stays green with its message unchanged.Regression guards (green before and after; not part of the red round; the orchestrator probes them before merge):
buildcallswrite_reportwhich callsFile.write;find_usercallssave_user;fetch_configcallsupdate!defined in the same file. Each still reports an offense.Origin
VERDICT: CREATE
Work started on branch ticket-1380 (os-sdlc implement, AFK unit).
Lens proposal (contract-auditor A12, challenge verdict: code defect found) not filed; ticket-skeptic DROP: speculative hardening. WriteChain#body_mutates? and WriteChainOffense#concrete_write_call both scan a def body for a WRITE_NAME send; one owner (for example WriteSends.first) would stop drift if a carve-out is added later.
Resolution
Done: Anchored QUERY_NAME to \A(for|find|fetch|get|build)(|\z) and WRITE_NAME to \A(save|persist|write|update)(|!|\z); WriteChain now counts a def as a write when its name matches or its body sends an anchored write verb (keeps resolve_and_persist -> project.save_tracker firing). Lesson signal updated. Three new true hits fixed by rename: os-backlog config_locator tests build_linked_worktree -> create_linked_worktree (writes a .git file); takedown eval run_cli_test build_cli -> create_cli (default paths write grader-calibration.json).
Evidence: Implementation 205 implementation_complete (commit
b02ed132); fixes1ea1559b,311ab69a; main pushed at311ab69a. os-sdlc suite 2861 runs 0 failures (31.6s); os-backlog suite 344 runs 0 failures. Plugin-wide cop hits over 2748 Ruby files (incl. extensionless scripts): before 1 (false, os-context/audit/bin/extract), after 0. Regression guards probed on main: build->write_report->File.write, find_user->save_user, fetch_config->update! each still fire.Follow-ups: #1449 widening is unblocked. Red-round loop recorded as occurrence on #859. Lens proposal (A12 duplicate body-send scan) dropped by ticket-skeptic as speculative; noted in a comment here.