T-HQ56-safe-grep-helper-for-ac-shell
Status: closed/superseded · Impact: medium · Complexity: small
Auto-generated from a /sdlc:task-work post-mortem. Review and
promote to open/ready before picking up.
Acceptance criteria that take the shape “grep X across this
directory returns zero matches” are common (negative-presence ACs:
“no caller writes raw git push”, “no script imports the legacy
module”, etc.). Each one currently re-derives its own exclusions
for test files, vendored dependencies, and build artifacts. The
footgun is live: the lease-CLI’s AC-10 shipped twice — the first
cut greped recursively and matched a test file’s docstring
containing “git push” plus compiled bytecode
(__pycache__/*.pyc). A shared helper with safe defaults would
prevent that across all future grep-based ACs. Cite
T-QC31-add-sdlc-lease-cli-commands for the live incident.
| Location | Role today |
|---|---|
apps/sdlc/lib/services/*/ops/ | The op-verb roster (D-H7FS-op-substrate-surface). No verb covers “grep these source files, excluding the obvious noise.” |
Grep-based ACs across docs/planning/tasks/ | Each negative-presence AC spells out its own exclusions inline in the AC text; the exclusion logic is per-AC and nothing is shared. |
Proposed
Section titled “Proposed”A new sdlc CLI op verb — deterministic ops are CLI verbs per
D-H7FS-op-substrate-surface; proposed home sdlc project safe-grep, path confirmed at definition — that exposes:
sdlc project safe-grep <pattern> <dir>— recursive grep with sane defaults: excludestests/,*.test.ts,node_modules/,.git/,dist/, and*.lock; returns matches in a stablepath:line:textshape; exits 0 on no-match and 1 on match, so an AC invocation reads the exit code directly.- An
--include-testsopt-in flag for the rare AC that actually wants to see test-file matches. - Honors an
allow-legacy-ref: <token>opt-in marker on individual source lines (any comment syntax). When a line carries this marker AND the line’s content contains the named<token>, the line is excluded from the match set. The marker names the exact token being excused (so reviewers can audit every excused line) and a free-form trailing comment captures the reason. This subsumes T-AVDD-ac-grep-allow-legacy-ref-marker. - A short guidance note explaining when an AC should reach for
this op vs raw
grep, and when to use theallow-legacy-ref:marker on a forbidden-token line that legitimately documents migration history.
Existing grep-based ACs (the lease AC-10 shape, any other negative- presence ACs we find in the planning corpus) get retrofitted in a follow-up sweep — out of scope here.
Approach
Section titled “Approach”- Survey the existing grep-based ACs in
docs/planning/tasks/to confirm the common exclusion set. Look for “grep” appearing in AC text. - Confirm the op’s home on the D-H7FS surface. Proposed:
project safe-grep— the grep runs over the consuming project’s tree, andprojectis the consuming-project hub. - Implement the op and its tests
(
apps/sdlc/lib/services/project/tests/safe-grep.test.ts). - Document in the op descriptor’s summary/help text, and add a
pointer for AC authors in the Acceptance-criteria guidance of
apps/sdlc/lib/model/entities/task/implementation-ready.md.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
apps/sdlc/lib/services/project/ops/safe-grep.ts | new | The op verb: recursive grep + standard exclusions + --include-tests opt-in + allow-legacy-ref filtering. |
apps/sdlc/lib/services/project/tests/safe-grep.test.ts | new | Tests covering: default exclusions hide test/vendored matches; pattern matches still surface in real source; --include-tests re-enables test-file matches; marker suppress/mismatch semantics. |
apps/sdlc/lib/model/entities/task/implementation-ready.md | modify | One-line pointer in the Acceptance-criteria guidance: reach for the op for negative-presence ACs. |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
sdlc project safe-grep "<some-pattern>" apps/sdlc/excludes matches undertests/,node_modules/, and.git/by default. - AC-2:
sdlc project safe-grep "<pattern>" <dir> --include-testsincludes test-file matches in the output. - AC-3: Exit code is 0 when there are no matches and 1 when
there are matches, so an AC can invoke the op directly as a
gate:
sdlc project safe-grep "git push" apps/sdlc/lib/services/lease/fails the check iff matches survive. - AC-4: At least one existing AC in the planning corpus is retrofitted to call the op as a proof of usability.
- AC-5: A source file containing the literal forbidden token on a
line carrying an
allow-legacy-ref: <token>marker (where<token>is the literal forbidden token) is excluded from the match set. A line containing the forbidden token without the marker is still matched. - AC-6: An
allow-legacy-ref:marker that names a different token than the line actually contains does NOT exclude the line — the marker must name the exact forbidden token being excused (audit trail). - AC-7: The op’s help text or the service README documents the marker shape and at least one worked example.
Out of scope
Section titled “Out of scope”- Sweeping every grep-based AC across
docs/planning/tasks/to retrofit them. Track that as a follow-up if AC-4 surfaces several candidates. - Building a richer code-search tool (e.g. AST-aware). The need here is just “grep with safer defaults”, not a full search index.
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”Spawned by /sdlc:task-work post-mortem of T-QC31-add-sdlc-lease-cli-commands on 2026-05-23.
Dedup search (spawn-from-post-mortem)
Section titled “Dedup search (spawn-from-post-mortem)”Bullet: AC-10 grep test footgun. First cut greped the CLI source directory recursively and matched the test file’s docstring containing ‘git push’ plus a compiled pycache/*.pyc. Fixed by greping only top-level .py files in the CLI dir (excluding tests/ and pycache/). Suggests: grep-based ACs should default to skipping test files and bytecode, OR a project-wide grep helper that already handles those exclusions would be safer than each AC re-deriving them. Keywords searched: project-wide, recursively, re-deriving, containing, grep-based, exclusions, directory, docstring Excluded: 2026-05-23-add-sdlc-lease-cli-commands Top candidates (score / status / headline):
- 14 / closed/superseded / 2026-05-20-setup-obsidian-backlog-dir-mismatch — Fix /sdlc:setup —obsidian writing backlog.base into singular backlog/ dir
- 10 / in-progress / 2026-05-23-add-lease-protocol-library-and-schemas — Add lease-protocol library and payload schemas
- 7 / closed/done / 2026-05-20-pluralize-recognizes-backlog-as-plural — pluralize() should treat ‘backlog’ as already-plural
- 7 / in-progress / 2026-05-22-move-plugin-runtime-state-to-sdlc-dir — Migrate sdlc plugin runtime state from .claude/ to .sdlc/
- 7 / closed/done / 2026-05-23-establish-plugin-lib-convention — Establish
plugin/lib/convention for Python libraries Decision: SPAWNED
Dedup search (spawn-from-post-mortem)
Section titled “Dedup search (spawn-from-post-mortem)”Bullet: AC-10’s grep guard tripped on a comment string. The pre-existing CLI test test_no_git_push_shells_in_cli does a bare substring grep for ‘git push’. Adding the namespace-guard wiring required a comment that contained the phrase (‘translate a git push plumbing error into …’); had to rephrase to ‘git-plumbing error’ to satisfy the grep. The substring-grep AC has known false-positive shape — see T-HQ56-safe-grep-helper-for-ac-shell (already spawned from the CLI task’s post-mortem) for the fix. Keywords searched: test_no_git_push_shells_in_cli, safe-grep-helper-for-ac-shell, namespace-guard, substring-grep, false-positive, pre-existing, git-plumbing, substring Excluded: 2026-05-23-add-lease-namespace-conflict-guard Top candidates (score / status / headline):
- 34 / closed/done / 2026-05-21-run-quality-checks-isolates-pre-existing-drift — run_quality_checks.py only fails on drift the current branch introduced
- 14 / planning/draft / 2026-05-21-audit-entities-baseline-allow — audit_entities.py: distinguish pre-existing drift from PR-introduced drift
- 13 / closed/done / 2026-05-21-pr-check-cursor-bootstrap-misses-existing-comments — pr-check cursor: pin bootstrap behavior and filter self-posted orchestrator comments
- 8 / planning/draft / 2026-05-23-dogfood-baseline-smoke-test — Dogfood smoke-test script for new quality-check verbs before declaring done
- 6 / planning/draft / 2026-05-23-start-task-drops-orphan-stashes — start_task.py detects pre-existing stashes referencing files absent on the rebase target and prompts to drop them Decision: LINKED-EXISTING 2026-05-24-safe-grep-helper-for-ac-shell Rationale: Bullet explicitly self-links to this already-spawned task (the AC-10 substring-grep footgun is precisely what this task fixes). Override the scorer’s SPAWNED verdict — keyword overlap on the top candidates is about pre-existing-drift handling in run_quality_checks, not the AC-shell grep helper. This is the canonical destination for the bullet. Linked from the post-mortem of T-K3RR-add-lease-namespace-conflict-guard on 2026-05-24.