agent-sdlc 0.3.0__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. agent_sdlc-0.4.0/PKG-INFO +205 -0
  2. agent_sdlc-0.4.0/README.md +190 -0
  3. agent_sdlc-0.4.0/agent_sdlc.egg-info/PKG-INFO +205 -0
  4. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/__init__.py +1 -1
  5. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/cli.py +13 -2
  6. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/no_leaks.py +5 -1
  7. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/runner.py +8 -2
  8. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/rules/tickets.md +6 -0
  9. agent_sdlc-0.4.0/tests/test_say_narrate_bin.py +35 -0
  10. agent_sdlc-0.3.0/PKG-INFO +0 -115
  11. agent_sdlc-0.3.0/README.md +0 -100
  12. agent_sdlc-0.3.0/agent_sdlc.egg-info/PKG-INFO +0 -115
  13. agent_sdlc-0.3.0/tests/test_say_narrate_bin.py +0 -26
  14. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/LICENSE +0 -0
  15. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/NOTICE.md +0 -0
  16. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/agent_sdlc.egg-info/SOURCES.txt +0 -0
  17. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/agent_sdlc.egg-info/dependency_links.txt +0 -0
  18. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/agent_sdlc.egg-info/entry_points.txt +0 -0
  19. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/agent_sdlc.egg-info/requires.txt +0 -0
  20. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/agent_sdlc.egg-info/top_level.txt +0 -0
  21. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/adversary.py +0 -0
  22. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/commit_msg.py +0 -0
  23. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/coverage_map.py +0 -0
  24. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/diff_discipline.py +0 -0
  25. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/model_vv.py +0 -0
  26. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/mutants.py +0 -0
  27. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/no_comments.py +0 -0
  28. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/no_new_docs.py +0 -0
  29. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/repo.py +0 -0
  30. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/rules.py +0 -0
  31. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/token.py +0 -0
  32. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/vocabulary.py +0 -0
  33. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/vocabulary_check.py +0 -0
  34. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/vocabulary_core.toml +0 -0
  35. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/vocabulary_molds.py +0 -0
  36. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/vocabulary_path.py +0 -0
  37. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/vocabulary_wordnet.py +0 -0
  38. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/mutation_gate/waivers.py +0 -0
  39. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/pyproject.toml +0 -0
  40. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/rules/code-clarity.md +0 -0
  41. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/rules/diff-discipline.md +0 -0
  42. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/rules/model-vv.md +0 -0
  43. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/rules/naming.md +0 -0
  44. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/rules/testing.md +0 -0
  45. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/rules/voice.md +0 -0
  46. {agent_sdlc-0.3.0 → agent_sdlc-0.4.0}/setup.cfg +0 -0
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-sdlc
3
+ Version: 0.4.0
4
+ Summary: Diff-scoped mutation gate for pre-commit and agent Stop hooks
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/PavelGuzenfeld/agent-sdlc
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ License-File: NOTICE.md
11
+ Requires-Dist: ast-grep-cli==0.45.3
12
+ Provides-Extra: vocabulary
13
+ Requires-Dist: nltk; extra == "vocabulary"
14
+ Dynamic: license-file
15
+
16
+ # agent-sdlc
17
+
18
+ [![CI](https://github.com/PavelGuzenfeld/agent-sdlc/actions/workflows/ci.yml/badge.svg)](https://github.com/PavelGuzenfeld/agent-sdlc/actions/workflows/ci.yml)
19
+
20
+ A software development lifecycle shipped as agent config for Claude Code and
21
+ Codex: rules, skills, slash commands, hooks, and a diff-scoped mutation gate.
22
+
23
+ Docs: <https://pavelguzenfeld.com/agent-sdlc/>
24
+
25
+ ## Why
26
+
27
+ A coding agent writes the code and the tests for it, and tests written next
28
+ to the code tend to agree with it, bugs included. We planted small realistic
29
+ bugs in a mature, well-tested project, and the suite stayed green for 57% of
30
+ them. Unit tests can each pass while the feature, entered the way a user
31
+ enters it, still fails. agent-sdlc puts those checks, plus scope, intent and
32
+ git safety, into hooks and a gate instead of leaving them to the agent's
33
+ memory.
34
+
35
+ | Problem | What catches it |
36
+ |---|---|
37
+ | Tests that pass but assert nothing | `mutation-gate`: a surviving mutant blocks the commit |
38
+ | Tests shaped to the code | An adversary review that sees intent and tests, never the code |
39
+ | Unit tests green, feature broken | One vertical-slice test per ticket, entered where a real consumer enters, red before any code |
40
+ | Scope creep | 40-line limit without a ticket; one ticket, one branch, one PR |
41
+ | Code bloat | Smallest-change and zero-comment rules; a `no-comments` hook; every added line must kill its mutants |
42
+ | Design notes rotting in the tree | New `.md` files are blocked; intent lives in the tracker |
43
+ | Destructive git | A hook that denies `reset --hard`, `add -A`, force-push |
44
+ | AI tells and leaked identity in commits | `commit-msg` and `no-leaks` hooks |
45
+
46
+ ## How it fits together
47
+
48
+ `x` marks a blocker. `[name]` is the rule in `rules/` that applies.
49
+
50
+ ```text
51
+ rules/*.md sync into .claude/rules and AGENTS.md; every session loads them
52
+
53
+ idea
54
+ | /grill, or gh issue create
55
+ v
56
+ +--------------------------+
57
+ | ticket | intent lives here, not in the tree [tickets]
58
+ +--------------------------+
59
+ | triage: model:* label + assignee
60
+ v x assigned to someone else: /kata stops
61
+ unlabelled or unassigned: /kata asks first
62
+ +--------------------------+
63
+ | /kata N | one ticket, one branch, one PR [tickets]
64
+ | branch off origin/main |
65
+ +--------------------------+
66
+ v
67
+ +--------------------------+
68
+ | red slice test | enters where a user enters, [testing]
69
+ | | watched failing first
70
+ +--------------------------+
71
+ v
72
+ +--------------------------+
73
+ | implement | smallest change [diff-discipline]
74
+ | | no comments, canonical names
75
+ +--------------------------+ [code-clarity] [naming]
76
+ | every Bash call: git-guardrail x reset --hard, add -A, force-push
77
+ v
78
+ +--------------------------+
79
+ | git commit |
80
+ | pre-commit |
81
+ | rules-check | x .claude/rules drifted from the pack
82
+ | mutation-gate ---------+- x surviving mutant --> new test, or waiver
83
+ | no-new-docs | x new .md file not allowlisted
84
+ | commit-msg |
85
+ | commit-msg | x banned words, AI attribution [voice]
86
+ | diff-discipline | x over 40 lines, no ticket ref
87
+ | no-leaks | x email, private IP, home path
88
+ +--------------------------+
89
+ | gate green -> adversary review: sees intent and tests, never
90
+ | the code; reports, never blocks; answer every finding
91
+ | Stop hook: runs the same gate if no commit did
92
+ v
93
+ +--------------------------+
94
+ | push, PR "Closes #N" | x CI red
95
+ +--------------------------+
96
+ v
97
+ +--------------------------+
98
+ | you review, type LGTM | squash merge, branch deleted
99
+ +--------------------------+
100
+ v
101
+ /done: handoff, follow-up tickets --> back to "ticket"
102
+ ```
103
+
104
+ ## Example
105
+
106
+ ```python
107
+ def is_adult(age):
108
+ return age >= 18
109
+
110
+ def test_adult():
111
+ assert is_adult(30)
112
+
113
+ def test_child():
114
+ assert not is_adult(5)
115
+ ```
116
+
117
+ ```text
118
+ BLOCKED: 2 mutant(s) survived with no waiver.
119
+ age.py:2:11:operator:age >= 18 => age > 18
120
+ age.py:2:18:literal:18 => 19
121
+ ```
122
+
123
+ Add `assert is_adult(18)` and `assert not is_adult(17)`, and the gate passes.
124
+
125
+ ## Install
126
+
127
+ Claude Code:
128
+
129
+ ```bash
130
+ claude plugin marketplace add PavelGuzenfeld/agent-sdlc
131
+ claude plugin install agent-sdlc@agent-sdlc
132
+ ```
133
+
134
+ Tools the gate needs, and Codex:
135
+
136
+ ```bash
137
+ git clone https://github.com/PavelGuzenfeld/agent-sdlc
138
+ cd agent-sdlc
139
+ ./install.sh --deps
140
+ ```
141
+
142
+ | Want | Do |
143
+ |---|---|
144
+ | Claude Code | The plugin above; `--scope local` or `--scope project` keeps it to one repo |
145
+ | Codex | `./install.sh --target codex`, or add the repo as a Codex marketplace source |
146
+ | Gate dependencies | `./install.sh --deps` (`--deps=say` adds voice) |
147
+ | Only the gate | `pip install agent-sdlc` (WordNet check: `agent-sdlc[vocabulary]`) |
148
+ | The gate on a repo | Add `.mutation-gate.toml`, wire the pre-commit hooks |
149
+
150
+ - `install.sh` links `skills/` into `~/.codex` and renders `commands/` as
151
+ Codex skills. It installs nothing into `~/.claude`. A second run changes
152
+ nothing.
153
+ - Installed an older `install.sh` into `~/.claude`? `./install.sh
154
+ --uninstall-legacy` removes those links and hooks, or every hook fires twice.
155
+ - `ast-grep-cli` adds an `sg` shim that can shadow the system `sg`; call
156
+ `ast-grep`.
157
+
158
+ ## Use it
159
+
160
+ In Claude Code (Codex: `$name` instead of `/name`):
161
+
162
+ 1. Install the plugin and `./install.sh --deps`
163
+ 2. In your repo: `touch .mutation-gate.toml && mutation-gate rules sync`
164
+ 3. `claude`, then `/grill <idea>` or `gh issue create …`
165
+ 4. Approve the ticket: `gh issue edit 42 --add-label model:sonnet`
166
+ 5. `/kata 42`: a worker writes the failing slice test, implements, gates, opens the PR
167
+ 6. Review, then type `LGTM`: it squash-merges and cleans up
168
+ 7. `/done` to close out the session
169
+
170
+ Full walkthrough, and what differs in Codex:
171
+ <https://pavelguzenfeld.com/agent-sdlc/usage/>
172
+
173
+ ## With standard
174
+
175
+ [standard](https://github.com/PavelGuzenfeld/standard) runs C++ and Python
176
+ quality gates on the PR. agent-sdlc runs before the commit. Use both:
177
+
178
+ ```text
179
+ agent session -> git commit -> PR -> merge
180
+ '------ agent-sdlc -------' '-- standard --'
181
+ ```
182
+
183
+ Run standard's setup first, then `mutation-gate rules sync`. Copying its
184
+ `AGENTS.md` afterwards drops the rules block. In CI, the agent-sdlc diff
185
+ checks need `--range`, because `pre-commit run --from-ref` doesn't enforce
186
+ them. Setup order, the CI job and the `docs/` allowlist:
187
+ <https://pavelguzenfeld.com/agent-sdlc/usage/#with-standard>
188
+
189
+ ## What's inside
190
+
191
+ | Path | What |
192
+ |---|---|
193
+ | `rules/` | How to work; synced into a repo with `mutation-gate rules sync` |
194
+ | `commands/` | Slash commands: `/kata`, `/done`, `/grill`, `/rectify` … |
195
+ | `skills/` | On-demand playbooks: `/diagnose`, `/sol-budget`, `/verify-generated-diff` … |
196
+ | `bin/` | Hooks and helpers: `git-guardrail.sh`, the `/say` stack |
197
+ | `mutation_gate/` | The gate and the `mutation-gate` CLI |
198
+
199
+ CI runs `scripts/no-leaks.sh` on every PR. It flags emails, RFC1918
200
+ addresses, user-at-host references, `/home/<user>/` paths and non-personal
201
+ `ghcr.io/` paths, and prints only `file:line`.
202
+
203
+ MIT, see [LICENSE](LICENSE) and [NOTICE.md](NOTICE.md) for one vendored
204
+ third-party skill. Contributions: [CONTRIBUTING.md](CONTRIBUTING.md), bound
205
+ by [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). Security: [SECURITY.md](SECURITY.md).
@@ -0,0 +1,190 @@
1
+ # agent-sdlc
2
+
3
+ [![CI](https://github.com/PavelGuzenfeld/agent-sdlc/actions/workflows/ci.yml/badge.svg)](https://github.com/PavelGuzenfeld/agent-sdlc/actions/workflows/ci.yml)
4
+
5
+ A software development lifecycle shipped as agent config for Claude Code and
6
+ Codex: rules, skills, slash commands, hooks, and a diff-scoped mutation gate.
7
+
8
+ Docs: <https://pavelguzenfeld.com/agent-sdlc/>
9
+
10
+ ## Why
11
+
12
+ A coding agent writes the code and the tests for it, and tests written next
13
+ to the code tend to agree with it, bugs included. We planted small realistic
14
+ bugs in a mature, well-tested project, and the suite stayed green for 57% of
15
+ them. Unit tests can each pass while the feature, entered the way a user
16
+ enters it, still fails. agent-sdlc puts those checks, plus scope, intent and
17
+ git safety, into hooks and a gate instead of leaving them to the agent's
18
+ memory.
19
+
20
+ | Problem | What catches it |
21
+ |---|---|
22
+ | Tests that pass but assert nothing | `mutation-gate`: a surviving mutant blocks the commit |
23
+ | Tests shaped to the code | An adversary review that sees intent and tests, never the code |
24
+ | Unit tests green, feature broken | One vertical-slice test per ticket, entered where a real consumer enters, red before any code |
25
+ | Scope creep | 40-line limit without a ticket; one ticket, one branch, one PR |
26
+ | Code bloat | Smallest-change and zero-comment rules; a `no-comments` hook; every added line must kill its mutants |
27
+ | Design notes rotting in the tree | New `.md` files are blocked; intent lives in the tracker |
28
+ | Destructive git | A hook that denies `reset --hard`, `add -A`, force-push |
29
+ | AI tells and leaked identity in commits | `commit-msg` and `no-leaks` hooks |
30
+
31
+ ## How it fits together
32
+
33
+ `x` marks a blocker. `[name]` is the rule in `rules/` that applies.
34
+
35
+ ```text
36
+ rules/*.md sync into .claude/rules and AGENTS.md; every session loads them
37
+
38
+ idea
39
+ | /grill, or gh issue create
40
+ v
41
+ +--------------------------+
42
+ | ticket | intent lives here, not in the tree [tickets]
43
+ +--------------------------+
44
+ | triage: model:* label + assignee
45
+ v x assigned to someone else: /kata stops
46
+ unlabelled or unassigned: /kata asks first
47
+ +--------------------------+
48
+ | /kata N | one ticket, one branch, one PR [tickets]
49
+ | branch off origin/main |
50
+ +--------------------------+
51
+ v
52
+ +--------------------------+
53
+ | red slice test | enters where a user enters, [testing]
54
+ | | watched failing first
55
+ +--------------------------+
56
+ v
57
+ +--------------------------+
58
+ | implement | smallest change [diff-discipline]
59
+ | | no comments, canonical names
60
+ +--------------------------+ [code-clarity] [naming]
61
+ | every Bash call: git-guardrail x reset --hard, add -A, force-push
62
+ v
63
+ +--------------------------+
64
+ | git commit |
65
+ | pre-commit |
66
+ | rules-check | x .claude/rules drifted from the pack
67
+ | mutation-gate ---------+- x surviving mutant --> new test, or waiver
68
+ | no-new-docs | x new .md file not allowlisted
69
+ | commit-msg |
70
+ | commit-msg | x banned words, AI attribution [voice]
71
+ | diff-discipline | x over 40 lines, no ticket ref
72
+ | no-leaks | x email, private IP, home path
73
+ +--------------------------+
74
+ | gate green -> adversary review: sees intent and tests, never
75
+ | the code; reports, never blocks; answer every finding
76
+ | Stop hook: runs the same gate if no commit did
77
+ v
78
+ +--------------------------+
79
+ | push, PR "Closes #N" | x CI red
80
+ +--------------------------+
81
+ v
82
+ +--------------------------+
83
+ | you review, type LGTM | squash merge, branch deleted
84
+ +--------------------------+
85
+ v
86
+ /done: handoff, follow-up tickets --> back to "ticket"
87
+ ```
88
+
89
+ ## Example
90
+
91
+ ```python
92
+ def is_adult(age):
93
+ return age >= 18
94
+
95
+ def test_adult():
96
+ assert is_adult(30)
97
+
98
+ def test_child():
99
+ assert not is_adult(5)
100
+ ```
101
+
102
+ ```text
103
+ BLOCKED: 2 mutant(s) survived with no waiver.
104
+ age.py:2:11:operator:age >= 18 => age > 18
105
+ age.py:2:18:literal:18 => 19
106
+ ```
107
+
108
+ Add `assert is_adult(18)` and `assert not is_adult(17)`, and the gate passes.
109
+
110
+ ## Install
111
+
112
+ Claude Code:
113
+
114
+ ```bash
115
+ claude plugin marketplace add PavelGuzenfeld/agent-sdlc
116
+ claude plugin install agent-sdlc@agent-sdlc
117
+ ```
118
+
119
+ Tools the gate needs, and Codex:
120
+
121
+ ```bash
122
+ git clone https://github.com/PavelGuzenfeld/agent-sdlc
123
+ cd agent-sdlc
124
+ ./install.sh --deps
125
+ ```
126
+
127
+ | Want | Do |
128
+ |---|---|
129
+ | Claude Code | The plugin above; `--scope local` or `--scope project` keeps it to one repo |
130
+ | Codex | `./install.sh --target codex`, or add the repo as a Codex marketplace source |
131
+ | Gate dependencies | `./install.sh --deps` (`--deps=say` adds voice) |
132
+ | Only the gate | `pip install agent-sdlc` (WordNet check: `agent-sdlc[vocabulary]`) |
133
+ | The gate on a repo | Add `.mutation-gate.toml`, wire the pre-commit hooks |
134
+
135
+ - `install.sh` links `skills/` into `~/.codex` and renders `commands/` as
136
+ Codex skills. It installs nothing into `~/.claude`. A second run changes
137
+ nothing.
138
+ - Installed an older `install.sh` into `~/.claude`? `./install.sh
139
+ --uninstall-legacy` removes those links and hooks, or every hook fires twice.
140
+ - `ast-grep-cli` adds an `sg` shim that can shadow the system `sg`; call
141
+ `ast-grep`.
142
+
143
+ ## Use it
144
+
145
+ In Claude Code (Codex: `$name` instead of `/name`):
146
+
147
+ 1. Install the plugin and `./install.sh --deps`
148
+ 2. In your repo: `touch .mutation-gate.toml && mutation-gate rules sync`
149
+ 3. `claude`, then `/grill <idea>` or `gh issue create …`
150
+ 4. Approve the ticket: `gh issue edit 42 --add-label model:sonnet`
151
+ 5. `/kata 42`: a worker writes the failing slice test, implements, gates, opens the PR
152
+ 6. Review, then type `LGTM`: it squash-merges and cleans up
153
+ 7. `/done` to close out the session
154
+
155
+ Full walkthrough, and what differs in Codex:
156
+ <https://pavelguzenfeld.com/agent-sdlc/usage/>
157
+
158
+ ## With standard
159
+
160
+ [standard](https://github.com/PavelGuzenfeld/standard) runs C++ and Python
161
+ quality gates on the PR. agent-sdlc runs before the commit. Use both:
162
+
163
+ ```text
164
+ agent session -> git commit -> PR -> merge
165
+ '------ agent-sdlc -------' '-- standard --'
166
+ ```
167
+
168
+ Run standard's setup first, then `mutation-gate rules sync`. Copying its
169
+ `AGENTS.md` afterwards drops the rules block. In CI, the agent-sdlc diff
170
+ checks need `--range`, because `pre-commit run --from-ref` doesn't enforce
171
+ them. Setup order, the CI job and the `docs/` allowlist:
172
+ <https://pavelguzenfeld.com/agent-sdlc/usage/#with-standard>
173
+
174
+ ## What's inside
175
+
176
+ | Path | What |
177
+ |---|---|
178
+ | `rules/` | How to work; synced into a repo with `mutation-gate rules sync` |
179
+ | `commands/` | Slash commands: `/kata`, `/done`, `/grill`, `/rectify` … |
180
+ | `skills/` | On-demand playbooks: `/diagnose`, `/sol-budget`, `/verify-generated-diff` … |
181
+ | `bin/` | Hooks and helpers: `git-guardrail.sh`, the `/say` stack |
182
+ | `mutation_gate/` | The gate and the `mutation-gate` CLI |
183
+
184
+ CI runs `scripts/no-leaks.sh` on every PR. It flags emails, RFC1918
185
+ addresses, user-at-host references, `/home/<user>/` paths and non-personal
186
+ `ghcr.io/` paths, and prints only `file:line`.
187
+
188
+ MIT, see [LICENSE](LICENSE) and [NOTICE.md](NOTICE.md) for one vendored
189
+ third-party skill. Contributions: [CONTRIBUTING.md](CONTRIBUTING.md), bound
190
+ by [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). Security: [SECURITY.md](SECURITY.md).
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-sdlc
3
+ Version: 0.4.0
4
+ Summary: Diff-scoped mutation gate for pre-commit and agent Stop hooks
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/PavelGuzenfeld/agent-sdlc
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ License-File: NOTICE.md
11
+ Requires-Dist: ast-grep-cli==0.45.3
12
+ Provides-Extra: vocabulary
13
+ Requires-Dist: nltk; extra == "vocabulary"
14
+ Dynamic: license-file
15
+
16
+ # agent-sdlc
17
+
18
+ [![CI](https://github.com/PavelGuzenfeld/agent-sdlc/actions/workflows/ci.yml/badge.svg)](https://github.com/PavelGuzenfeld/agent-sdlc/actions/workflows/ci.yml)
19
+
20
+ A software development lifecycle shipped as agent config for Claude Code and
21
+ Codex: rules, skills, slash commands, hooks, and a diff-scoped mutation gate.
22
+
23
+ Docs: <https://pavelguzenfeld.com/agent-sdlc/>
24
+
25
+ ## Why
26
+
27
+ A coding agent writes the code and the tests for it, and tests written next
28
+ to the code tend to agree with it, bugs included. We planted small realistic
29
+ bugs in a mature, well-tested project, and the suite stayed green for 57% of
30
+ them. Unit tests can each pass while the feature, entered the way a user
31
+ enters it, still fails. agent-sdlc puts those checks, plus scope, intent and
32
+ git safety, into hooks and a gate instead of leaving them to the agent's
33
+ memory.
34
+
35
+ | Problem | What catches it |
36
+ |---|---|
37
+ | Tests that pass but assert nothing | `mutation-gate`: a surviving mutant blocks the commit |
38
+ | Tests shaped to the code | An adversary review that sees intent and tests, never the code |
39
+ | Unit tests green, feature broken | One vertical-slice test per ticket, entered where a real consumer enters, red before any code |
40
+ | Scope creep | 40-line limit without a ticket; one ticket, one branch, one PR |
41
+ | Code bloat | Smallest-change and zero-comment rules; a `no-comments` hook; every added line must kill its mutants |
42
+ | Design notes rotting in the tree | New `.md` files are blocked; intent lives in the tracker |
43
+ | Destructive git | A hook that denies `reset --hard`, `add -A`, force-push |
44
+ | AI tells and leaked identity in commits | `commit-msg` and `no-leaks` hooks |
45
+
46
+ ## How it fits together
47
+
48
+ `x` marks a blocker. `[name]` is the rule in `rules/` that applies.
49
+
50
+ ```text
51
+ rules/*.md sync into .claude/rules and AGENTS.md; every session loads them
52
+
53
+ idea
54
+ | /grill, or gh issue create
55
+ v
56
+ +--------------------------+
57
+ | ticket | intent lives here, not in the tree [tickets]
58
+ +--------------------------+
59
+ | triage: model:* label + assignee
60
+ v x assigned to someone else: /kata stops
61
+ unlabelled or unassigned: /kata asks first
62
+ +--------------------------+
63
+ | /kata N | one ticket, one branch, one PR [tickets]
64
+ | branch off origin/main |
65
+ +--------------------------+
66
+ v
67
+ +--------------------------+
68
+ | red slice test | enters where a user enters, [testing]
69
+ | | watched failing first
70
+ +--------------------------+
71
+ v
72
+ +--------------------------+
73
+ | implement | smallest change [diff-discipline]
74
+ | | no comments, canonical names
75
+ +--------------------------+ [code-clarity] [naming]
76
+ | every Bash call: git-guardrail x reset --hard, add -A, force-push
77
+ v
78
+ +--------------------------+
79
+ | git commit |
80
+ | pre-commit |
81
+ | rules-check | x .claude/rules drifted from the pack
82
+ | mutation-gate ---------+- x surviving mutant --> new test, or waiver
83
+ | no-new-docs | x new .md file not allowlisted
84
+ | commit-msg |
85
+ | commit-msg | x banned words, AI attribution [voice]
86
+ | diff-discipline | x over 40 lines, no ticket ref
87
+ | no-leaks | x email, private IP, home path
88
+ +--------------------------+
89
+ | gate green -> adversary review: sees intent and tests, never
90
+ | the code; reports, never blocks; answer every finding
91
+ | Stop hook: runs the same gate if no commit did
92
+ v
93
+ +--------------------------+
94
+ | push, PR "Closes #N" | x CI red
95
+ +--------------------------+
96
+ v
97
+ +--------------------------+
98
+ | you review, type LGTM | squash merge, branch deleted
99
+ +--------------------------+
100
+ v
101
+ /done: handoff, follow-up tickets --> back to "ticket"
102
+ ```
103
+
104
+ ## Example
105
+
106
+ ```python
107
+ def is_adult(age):
108
+ return age >= 18
109
+
110
+ def test_adult():
111
+ assert is_adult(30)
112
+
113
+ def test_child():
114
+ assert not is_adult(5)
115
+ ```
116
+
117
+ ```text
118
+ BLOCKED: 2 mutant(s) survived with no waiver.
119
+ age.py:2:11:operator:age >= 18 => age > 18
120
+ age.py:2:18:literal:18 => 19
121
+ ```
122
+
123
+ Add `assert is_adult(18)` and `assert not is_adult(17)`, and the gate passes.
124
+
125
+ ## Install
126
+
127
+ Claude Code:
128
+
129
+ ```bash
130
+ claude plugin marketplace add PavelGuzenfeld/agent-sdlc
131
+ claude plugin install agent-sdlc@agent-sdlc
132
+ ```
133
+
134
+ Tools the gate needs, and Codex:
135
+
136
+ ```bash
137
+ git clone https://github.com/PavelGuzenfeld/agent-sdlc
138
+ cd agent-sdlc
139
+ ./install.sh --deps
140
+ ```
141
+
142
+ | Want | Do |
143
+ |---|---|
144
+ | Claude Code | The plugin above; `--scope local` or `--scope project` keeps it to one repo |
145
+ | Codex | `./install.sh --target codex`, or add the repo as a Codex marketplace source |
146
+ | Gate dependencies | `./install.sh --deps` (`--deps=say` adds voice) |
147
+ | Only the gate | `pip install agent-sdlc` (WordNet check: `agent-sdlc[vocabulary]`) |
148
+ | The gate on a repo | Add `.mutation-gate.toml`, wire the pre-commit hooks |
149
+
150
+ - `install.sh` links `skills/` into `~/.codex` and renders `commands/` as
151
+ Codex skills. It installs nothing into `~/.claude`. A second run changes
152
+ nothing.
153
+ - Installed an older `install.sh` into `~/.claude`? `./install.sh
154
+ --uninstall-legacy` removes those links and hooks, or every hook fires twice.
155
+ - `ast-grep-cli` adds an `sg` shim that can shadow the system `sg`; call
156
+ `ast-grep`.
157
+
158
+ ## Use it
159
+
160
+ In Claude Code (Codex: `$name` instead of `/name`):
161
+
162
+ 1. Install the plugin and `./install.sh --deps`
163
+ 2. In your repo: `touch .mutation-gate.toml && mutation-gate rules sync`
164
+ 3. `claude`, then `/grill <idea>` or `gh issue create …`
165
+ 4. Approve the ticket: `gh issue edit 42 --add-label model:sonnet`
166
+ 5. `/kata 42`: a worker writes the failing slice test, implements, gates, opens the PR
167
+ 6. Review, then type `LGTM`: it squash-merges and cleans up
168
+ 7. `/done` to close out the session
169
+
170
+ Full walkthrough, and what differs in Codex:
171
+ <https://pavelguzenfeld.com/agent-sdlc/usage/>
172
+
173
+ ## With standard
174
+
175
+ [standard](https://github.com/PavelGuzenfeld/standard) runs C++ and Python
176
+ quality gates on the PR. agent-sdlc runs before the commit. Use both:
177
+
178
+ ```text
179
+ agent session -> git commit -> PR -> merge
180
+ '------ agent-sdlc -------' '-- standard --'
181
+ ```
182
+
183
+ Run standard's setup first, then `mutation-gate rules sync`. Copying its
184
+ `AGENTS.md` afterwards drops the rules block. In CI, the agent-sdlc diff
185
+ checks need `--range`, because `pre-commit run --from-ref` doesn't enforce
186
+ them. Setup order, the CI job and the `docs/` allowlist:
187
+ <https://pavelguzenfeld.com/agent-sdlc/usage/#with-standard>
188
+
189
+ ## What's inside
190
+
191
+ | Path | What |
192
+ |---|---|
193
+ | `rules/` | How to work; synced into a repo with `mutation-gate rules sync` |
194
+ | `commands/` | Slash commands: `/kata`, `/done`, `/grill`, `/rectify` … |
195
+ | `skills/` | On-demand playbooks: `/diagnose`, `/sol-budget`, `/verify-generated-diff` … |
196
+ | `bin/` | Hooks and helpers: `git-guardrail.sh`, the `/say` stack |
197
+ | `mutation_gate/` | The gate and the `mutation-gate` CLI |
198
+
199
+ CI runs `scripts/no-leaks.sh` on every PR. It flags emails, RFC1918
200
+ addresses, user-at-host references, `/home/<user>/` paths and non-personal
201
+ `ghcr.io/` paths, and prints only `file:line`.
202
+
203
+ MIT, see [LICENSE](LICENSE) and [NOTICE.md](NOTICE.md) for one vendored
204
+ third-party skill. Contributions: [CONTRIBUTING.md](CONTRIBUTING.md), bound
205
+ by [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). Security: [SECURITY.md](SECURITY.md).
@@ -1,3 +1,3 @@
1
1
  """Diff-scoped mutation gate. Spec: PavelGuzenfeld/dotfiles#9."""
2
2
 
3
- __version__ = "0.3.0"
3
+ __version__ = "0.4.0"
@@ -11,6 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  import argparse
13
13
  import json
14
+ import select
14
15
  import sys
15
16
  from datetime import datetime, timezone
16
17
  from pathlib import Path
@@ -20,6 +21,7 @@ from .repo import CACHE_ROOT, CONFIG_NAME, GateError, discover, git, skip_reason
20
21
 
21
22
 
22
23
  TIMEOUT_FACTOR = 6.0
24
+ STOP_HOOK_PAYLOAD_WAIT_SECONDS = 1.0
23
25
 
24
26
 
25
27
  def _emit(line: str = "") -> None:
@@ -154,10 +156,19 @@ def _gate_file(
154
156
  return blocked, survivors, cands
155
157
 
156
158
 
159
+ def _stdin_is_ready() -> bool:
160
+ try:
161
+ readable, _, _ = select.select([sys.stdin], [], [], STOP_HOOK_PAYLOAD_WAIT_SECONDS)
162
+ except (ValueError, OSError):
163
+ return True
164
+ return bool(readable)
165
+
166
+
157
167
  def _stop_hook_payload() -> dict:
158
168
  """The Stop hook's JSON on stdin carries `cwd` (#68 item 1) and
159
- `transcript_path` (#100) — read once, since stdin is a one-shot stream."""
160
- if sys.stdin.isatty():
169
+ `transcript_path` (#100) — read once, since stdin is a one-shot stream.
170
+ An agent shell's stdin is an open pipe that never writes or closes (#337)."""
171
+ if sys.stdin.isatty() or not _stdin_is_ready():
161
172
  return {}
162
173
  try:
163
174
  payload = json.loads(sys.stdin.read())
@@ -39,6 +39,9 @@ _GIT_SSH_CLONE_RE = re.compile(
39
39
  _NPM_VERSION_RE = re.compile(
40
40
  r"(^|[^A-Za-z0-9._%+/-])[A-Za-z0-9._%+-]+@[0-9]+\.[0-9]+\.[0-9]+([^0-9.]|$)"
41
41
  )
42
+ _CLAUDE_PLUGIN_ID_RE = re.compile(
43
+ r"(^|[^A-Za-z0-9._%+/-])[A-Za-z0-9._-]+@[a]gent-sdlc([^A-Za-z0-9.-]|$)"
44
+ )
42
45
 
43
46
  _BULLET_RE = re.compile(r"^\s*-\s*")
44
47
  _ARROW = "→"
@@ -54,7 +57,8 @@ class BannedName:
54
57
 
55
58
  def _carve_out(line: str) -> str:
56
59
  line = _GIT_SSH_CLONE_RE.sub(r"\1public-git-ssh-clone-url", line)
57
- return _NPM_VERSION_RE.sub(r"\1npm-package-version\2", line)
60
+ line = _NPM_VERSION_RE.sub(r"\1npm-package-version\2", line)
61
+ return _CLAUDE_PLUGIN_ID_RE.sub(r"\1claude-plugin-id\2", line)
58
62
 
59
63
 
60
64
  def generic_hit(line: str) -> bool:
@@ -45,18 +45,24 @@ TERM_GRACE_SECONDS = 15.0
45
45
 
46
46
  DOCKER_RUN_RE = re.compile(r"\bdocker\s+run\b")
47
47
 
48
+ UNWRITTEN_PYCACHE = "/nonexistent/mutation-gate-pycache"
49
+
48
50
  # Root in the container writes these into the bind-mounted repo as directories a
49
51
  # host user then cannot rm without starting another container (#74).
50
- NO_ROOT_CACHE = "-e PYTHONDONTWRITEBYTECODE=1 -e PYTEST_ADDOPTS='-p no:cacheprovider'"
52
+ NO_ROOT_CACHE = (
53
+ f"-e PYTHONDONTWRITEBYTECODE=1 -e PYTHONPYCACHEPREFIX={UNWRITTEN_PYCACHE}"
54
+ " -e PYTEST_ADDOPTS='-p no:cacheprovider'"
55
+ )
51
56
 
52
57
  GIT_REPO_SCOPED = ("GIT_DIR", "GIT_INDEX_FILE", "GIT_WORK_TREE")
58
+ NO_BYTECODE = {"PYTHONDONTWRITEBYTECODE": "1", "PYTHONPYCACHEPREFIX": UNWRITTEN_PYCACHE}
53
59
 
54
60
 
55
61
  def _test_env() -> dict[str, str]:
56
62
  """The environment a repo's own command runs in. Git exports these into every
57
63
  hook and they override `-C`, so a suite that builds a temp repo stages into
58
64
  the commit being gated instead (flowdiff#83)."""
59
- return {k: v for k, v in os.environ.items() if k not in GIT_REPO_SCOPED}
65
+ return {k: v for k, v in os.environ.items() if k not in GIT_REPO_SCOPED} | NO_BYTECODE
60
66
 
61
67
 
62
68
  @dataclass
@@ -30,6 +30,12 @@ sets it alongside the `model:*` label — the label approves, the assignee
30
30
  names who works it. An agent works only tickets assigned to the `gh` user
31
31
  it runs as, `@me`, and its PR carries that same assignee.
32
32
 
33
+ A PR waits for CI in one foreground call — `gh pr checks <PR> --watch
34
+ --fail-fast` or `gh run watch <run> --exit-status` — with the Bash timeout at
35
+ its 600000 ms maximum and the output to a file, re-issued on timeout. The call
36
+ blocks until CI settles, so no turn is spent on a status check: every turn
37
+ re-reads the whole context.
38
+
33
39
  ## Follow-ups
34
40
 
35
41
  A follow-up starts from `/done`, a kata worker, any agent mid-task, or the
@@ -0,0 +1,35 @@
1
+ import importlib.util
2
+ import shutil
3
+ from pathlib import Path
4
+
5
+ MODULE_PATH = Path(__file__).resolve().parents[1] / "bin" / "say-narrate.py"
6
+
7
+
8
+ def _load_from(path: Path):
9
+ spec = importlib.util.spec_from_file_location("say_narrate_probe", path)
10
+ module = importlib.util.module_from_spec(spec)
11
+ spec.loader.exec_module(module)
12
+ return module
13
+
14
+
15
+ def test_bin_is_the_scripts_own_directory_under_a_plugin_only_install(monkeypatch, tmp_path):
16
+ monkeypatch.delenv("CLAUDE_PLUGIN_ROOT", raising=False)
17
+ monkeypatch.setenv("HOME", str(tmp_path / "home-without-claude"))
18
+ plugin_bin = tmp_path / "plugin" / "bin"
19
+ plugin_bin.mkdir(parents=True)
20
+ shutil.copy(MODULE_PATH, plugin_bin / "say-narrate.py")
21
+
22
+ module = _load_from(plugin_bin / "say-narrate.py")
23
+
24
+ assert module.BIN == str(plugin_bin)
25
+
26
+
27
+ def test_bin_ignores_a_plugin_root_that_points_somewhere_else(monkeypatch, tmp_path):
28
+ monkeypatch.setenv("CLAUDE_PLUGIN_ROOT", "/fake/plugin/root")
29
+ legacy_bin = tmp_path / ".claude" / "bin"
30
+ legacy_bin.mkdir(parents=True)
31
+ (legacy_bin / "say-narrate.py").symlink_to(MODULE_PATH)
32
+
33
+ module = _load_from(legacy_bin / "say-narrate.py")
34
+
35
+ assert module.BIN == str(legacy_bin)
agent_sdlc-0.3.0/PKG-INFO DELETED
@@ -1,115 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: agent-sdlc
3
- Version: 0.3.0
4
- Summary: Diff-scoped mutation gate for pre-commit and agent Stop hooks
5
- License-Expression: MIT
6
- Project-URL: Homepage, https://github.com/PavelGuzenfeld/agent-sdlc
7
- Requires-Python: >=3.11
8
- Description-Content-Type: text/markdown
9
- License-File: LICENSE
10
- License-File: NOTICE.md
11
- Requires-Dist: ast-grep-cli==0.45.3
12
- Provides-Extra: vocabulary
13
- Requires-Dist: nltk; extra == "vocabulary"
14
- Dynamic: license-file
15
-
16
- # agent-sdlc
17
-
18
- [![CI](https://github.com/PavelGuzenfeld/agent-sdlc/actions/workflows/ci.yml/badge.svg)](https://github.com/PavelGuzenfeld/agent-sdlc/actions/workflows/ci.yml)
19
-
20
- A software development lifecycle shipped as agent config for Claude Code and
21
- Codex: rules, skills, slash commands, hooks, and a diff-scoped mutation gate.
22
-
23
- Docs: <https://pavelguzenfeld.com/agent-sdlc/>
24
-
25
- ## Why
26
-
27
- A coding agent writes the code and the tests for it, and tests written next
28
- to the code tend to agree with it, bugs included. On a mature suite, 57% of
29
- real bug-class mutants survived. agent-sdlc puts that check, plus scope,
30
- intent and git safety, into hooks and a gate instead of leaving them to the
31
- agent's memory.
32
-
33
- | Problem | What catches it |
34
- |---|---|
35
- | Tests that pass but assert nothing | `mutation-gate`: a surviving mutant blocks the commit |
36
- | Tests shaped to the code | An adversary review that sees intent and tests, never the code |
37
- | Scope creep | 40-line limit without a ticket; one ticket, one branch, one PR |
38
- | Design notes rotting in the tree | New `.md` files are blocked; intent lives in the tracker |
39
- | Destructive git | A hook that denies `reset --hard`, `add -A`, force-push |
40
- | AI tells and leaked identity in commits | `commit-msg` and `no-leaks` hooks |
41
-
42
- ## Example
43
-
44
- ```python
45
- def is_adult(age):
46
- return age >= 18
47
-
48
- def test_adult():
49
- assert is_adult(30)
50
-
51
- def test_child():
52
- assert not is_adult(5)
53
- ```
54
-
55
- ```text
56
- BLOCKED: 2 mutant(s) survived with no waiver.
57
- age.py:2:11:operator:age >= 18 => age > 18
58
- age.py:2:18:literal:18 => 19
59
- ```
60
-
61
- Add `assert is_adult(18)` and `assert not is_adult(17)`, and the gate passes.
62
-
63
- ## Install
64
-
65
- ```bash
66
- git clone https://github.com/PavelGuzenfeld/agent-sdlc
67
- cd agent-sdlc
68
- ./install.sh --target all --deps
69
- ```
70
-
71
- | Want | Do |
72
- |---|---|
73
- | The whole pack | `./install.sh --target all --deps` (targets: `claude`, `codex`, `all`; `--deps=say` adds voice) |
74
- | Only the gate | `pip install agent-sdlc` (WordNet check: `agent-sdlc[vocabulary]`) |
75
- | A plugin | `claude --plugin-dir /path/to/agent-sdlc`, or add the repo as a Codex marketplace source |
76
- | The gate on a repo | Add `.mutation-gate.toml`, wire the pre-commit hooks |
77
-
78
- - `install.sh` links `skills/` into `~/.claude` and `~/.codex`, links
79
- `commands/` and `bin/` into `~/.claude`, renders `commands/` as Codex
80
- skills and merges the hooks. A second run changes nothing.
81
- - `ast-grep-cli` adds an `sg` shim that can shadow the system `sg`; call
82
- `ast-grep`.
83
-
84
- ## Use it
85
-
86
- In Claude Code (Codex: `$name` instead of `/name`):
87
-
88
- 1. `./install.sh --target claude --deps`
89
- 2. In your repo: `touch .mutation-gate.toml && mutation-gate rules sync`
90
- 3. `claude`, then `/grill <idea>` or `gh issue create …`
91
- 4. Approve the ticket: `gh issue edit 42 --add-label model:sonnet`
92
- 5. `/kata 42`: a worker writes the failing test, implements, gates, opens the PR
93
- 6. Review, then type `LGTM`: it squash-merges and cleans up
94
- 7. `/done` to close out the session
95
-
96
- Full walkthrough, and what differs in Codex:
97
- <https://pavelguzenfeld.com/agent-sdlc/usage/>
98
-
99
- ## What's inside
100
-
101
- | Path | What |
102
- |---|---|
103
- | `rules/` | How to work; synced into a repo with `mutation-gate rules sync` |
104
- | `commands/` | Slash commands: `/kata`, `/done`, `/grill`, `/rectify` … |
105
- | `skills/` | On-demand playbooks: `/diagnose`, `/sol-budget`, `/verify-generated-diff` … |
106
- | `bin/` | Hooks and helpers: `git-guardrail.sh`, the `/say` stack |
107
- | `mutation_gate/` | The gate and the `mutation-gate` CLI |
108
-
109
- CI runs `scripts/no-leaks.sh` on every PR. It flags emails, RFC1918
110
- addresses, user-at-host references, `/home/<user>/` paths and non-personal
111
- `ghcr.io/` paths, and prints only `file:line`.
112
-
113
- MIT, see [LICENSE](LICENSE) and [NOTICE.md](NOTICE.md) for one vendored
114
- third-party skill. Contributions: [CONTRIBUTING.md](CONTRIBUTING.md), bound
115
- by [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). Security: [SECURITY.md](SECURITY.md).
@@ -1,100 +0,0 @@
1
- # agent-sdlc
2
-
3
- [![CI](https://github.com/PavelGuzenfeld/agent-sdlc/actions/workflows/ci.yml/badge.svg)](https://github.com/PavelGuzenfeld/agent-sdlc/actions/workflows/ci.yml)
4
-
5
- A software development lifecycle shipped as agent config for Claude Code and
6
- Codex: rules, skills, slash commands, hooks, and a diff-scoped mutation gate.
7
-
8
- Docs: <https://pavelguzenfeld.com/agent-sdlc/>
9
-
10
- ## Why
11
-
12
- A coding agent writes the code and the tests for it, and tests written next
13
- to the code tend to agree with it, bugs included. On a mature suite, 57% of
14
- real bug-class mutants survived. agent-sdlc puts that check, plus scope,
15
- intent and git safety, into hooks and a gate instead of leaving them to the
16
- agent's memory.
17
-
18
- | Problem | What catches it |
19
- |---|---|
20
- | Tests that pass but assert nothing | `mutation-gate`: a surviving mutant blocks the commit |
21
- | Tests shaped to the code | An adversary review that sees intent and tests, never the code |
22
- | Scope creep | 40-line limit without a ticket; one ticket, one branch, one PR |
23
- | Design notes rotting in the tree | New `.md` files are blocked; intent lives in the tracker |
24
- | Destructive git | A hook that denies `reset --hard`, `add -A`, force-push |
25
- | AI tells and leaked identity in commits | `commit-msg` and `no-leaks` hooks |
26
-
27
- ## Example
28
-
29
- ```python
30
- def is_adult(age):
31
- return age >= 18
32
-
33
- def test_adult():
34
- assert is_adult(30)
35
-
36
- def test_child():
37
- assert not is_adult(5)
38
- ```
39
-
40
- ```text
41
- BLOCKED: 2 mutant(s) survived with no waiver.
42
- age.py:2:11:operator:age >= 18 => age > 18
43
- age.py:2:18:literal:18 => 19
44
- ```
45
-
46
- Add `assert is_adult(18)` and `assert not is_adult(17)`, and the gate passes.
47
-
48
- ## Install
49
-
50
- ```bash
51
- git clone https://github.com/PavelGuzenfeld/agent-sdlc
52
- cd agent-sdlc
53
- ./install.sh --target all --deps
54
- ```
55
-
56
- | Want | Do |
57
- |---|---|
58
- | The whole pack | `./install.sh --target all --deps` (targets: `claude`, `codex`, `all`; `--deps=say` adds voice) |
59
- | Only the gate | `pip install agent-sdlc` (WordNet check: `agent-sdlc[vocabulary]`) |
60
- | A plugin | `claude --plugin-dir /path/to/agent-sdlc`, or add the repo as a Codex marketplace source |
61
- | The gate on a repo | Add `.mutation-gate.toml`, wire the pre-commit hooks |
62
-
63
- - `install.sh` links `skills/` into `~/.claude` and `~/.codex`, links
64
- `commands/` and `bin/` into `~/.claude`, renders `commands/` as Codex
65
- skills and merges the hooks. A second run changes nothing.
66
- - `ast-grep-cli` adds an `sg` shim that can shadow the system `sg`; call
67
- `ast-grep`.
68
-
69
- ## Use it
70
-
71
- In Claude Code (Codex: `$name` instead of `/name`):
72
-
73
- 1. `./install.sh --target claude --deps`
74
- 2. In your repo: `touch .mutation-gate.toml && mutation-gate rules sync`
75
- 3. `claude`, then `/grill <idea>` or `gh issue create …`
76
- 4. Approve the ticket: `gh issue edit 42 --add-label model:sonnet`
77
- 5. `/kata 42`: a worker writes the failing test, implements, gates, opens the PR
78
- 6. Review, then type `LGTM`: it squash-merges and cleans up
79
- 7. `/done` to close out the session
80
-
81
- Full walkthrough, and what differs in Codex:
82
- <https://pavelguzenfeld.com/agent-sdlc/usage/>
83
-
84
- ## What's inside
85
-
86
- | Path | What |
87
- |---|---|
88
- | `rules/` | How to work; synced into a repo with `mutation-gate rules sync` |
89
- | `commands/` | Slash commands: `/kata`, `/done`, `/grill`, `/rectify` … |
90
- | `skills/` | On-demand playbooks: `/diagnose`, `/sol-budget`, `/verify-generated-diff` … |
91
- | `bin/` | Hooks and helpers: `git-guardrail.sh`, the `/say` stack |
92
- | `mutation_gate/` | The gate and the `mutation-gate` CLI |
93
-
94
- CI runs `scripts/no-leaks.sh` on every PR. It flags emails, RFC1918
95
- addresses, user-at-host references, `/home/<user>/` paths and non-personal
96
- `ghcr.io/` paths, and prints only `file:line`.
97
-
98
- MIT, see [LICENSE](LICENSE) and [NOTICE.md](NOTICE.md) for one vendored
99
- third-party skill. Contributions: [CONTRIBUTING.md](CONTRIBUTING.md), bound
100
- by [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). Security: [SECURITY.md](SECURITY.md).
@@ -1,115 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: agent-sdlc
3
- Version: 0.3.0
4
- Summary: Diff-scoped mutation gate for pre-commit and agent Stop hooks
5
- License-Expression: MIT
6
- Project-URL: Homepage, https://github.com/PavelGuzenfeld/agent-sdlc
7
- Requires-Python: >=3.11
8
- Description-Content-Type: text/markdown
9
- License-File: LICENSE
10
- License-File: NOTICE.md
11
- Requires-Dist: ast-grep-cli==0.45.3
12
- Provides-Extra: vocabulary
13
- Requires-Dist: nltk; extra == "vocabulary"
14
- Dynamic: license-file
15
-
16
- # agent-sdlc
17
-
18
- [![CI](https://github.com/PavelGuzenfeld/agent-sdlc/actions/workflows/ci.yml/badge.svg)](https://github.com/PavelGuzenfeld/agent-sdlc/actions/workflows/ci.yml)
19
-
20
- A software development lifecycle shipped as agent config for Claude Code and
21
- Codex: rules, skills, slash commands, hooks, and a diff-scoped mutation gate.
22
-
23
- Docs: <https://pavelguzenfeld.com/agent-sdlc/>
24
-
25
- ## Why
26
-
27
- A coding agent writes the code and the tests for it, and tests written next
28
- to the code tend to agree with it, bugs included. On a mature suite, 57% of
29
- real bug-class mutants survived. agent-sdlc puts that check, plus scope,
30
- intent and git safety, into hooks and a gate instead of leaving them to the
31
- agent's memory.
32
-
33
- | Problem | What catches it |
34
- |---|---|
35
- | Tests that pass but assert nothing | `mutation-gate`: a surviving mutant blocks the commit |
36
- | Tests shaped to the code | An adversary review that sees intent and tests, never the code |
37
- | Scope creep | 40-line limit without a ticket; one ticket, one branch, one PR |
38
- | Design notes rotting in the tree | New `.md` files are blocked; intent lives in the tracker |
39
- | Destructive git | A hook that denies `reset --hard`, `add -A`, force-push |
40
- | AI tells and leaked identity in commits | `commit-msg` and `no-leaks` hooks |
41
-
42
- ## Example
43
-
44
- ```python
45
- def is_adult(age):
46
- return age >= 18
47
-
48
- def test_adult():
49
- assert is_adult(30)
50
-
51
- def test_child():
52
- assert not is_adult(5)
53
- ```
54
-
55
- ```text
56
- BLOCKED: 2 mutant(s) survived with no waiver.
57
- age.py:2:11:operator:age >= 18 => age > 18
58
- age.py:2:18:literal:18 => 19
59
- ```
60
-
61
- Add `assert is_adult(18)` and `assert not is_adult(17)`, and the gate passes.
62
-
63
- ## Install
64
-
65
- ```bash
66
- git clone https://github.com/PavelGuzenfeld/agent-sdlc
67
- cd agent-sdlc
68
- ./install.sh --target all --deps
69
- ```
70
-
71
- | Want | Do |
72
- |---|---|
73
- | The whole pack | `./install.sh --target all --deps` (targets: `claude`, `codex`, `all`; `--deps=say` adds voice) |
74
- | Only the gate | `pip install agent-sdlc` (WordNet check: `agent-sdlc[vocabulary]`) |
75
- | A plugin | `claude --plugin-dir /path/to/agent-sdlc`, or add the repo as a Codex marketplace source |
76
- | The gate on a repo | Add `.mutation-gate.toml`, wire the pre-commit hooks |
77
-
78
- - `install.sh` links `skills/` into `~/.claude` and `~/.codex`, links
79
- `commands/` and `bin/` into `~/.claude`, renders `commands/` as Codex
80
- skills and merges the hooks. A second run changes nothing.
81
- - `ast-grep-cli` adds an `sg` shim that can shadow the system `sg`; call
82
- `ast-grep`.
83
-
84
- ## Use it
85
-
86
- In Claude Code (Codex: `$name` instead of `/name`):
87
-
88
- 1. `./install.sh --target claude --deps`
89
- 2. In your repo: `touch .mutation-gate.toml && mutation-gate rules sync`
90
- 3. `claude`, then `/grill <idea>` or `gh issue create …`
91
- 4. Approve the ticket: `gh issue edit 42 --add-label model:sonnet`
92
- 5. `/kata 42`: a worker writes the failing test, implements, gates, opens the PR
93
- 6. Review, then type `LGTM`: it squash-merges and cleans up
94
- 7. `/done` to close out the session
95
-
96
- Full walkthrough, and what differs in Codex:
97
- <https://pavelguzenfeld.com/agent-sdlc/usage/>
98
-
99
- ## What's inside
100
-
101
- | Path | What |
102
- |---|---|
103
- | `rules/` | How to work; synced into a repo with `mutation-gate rules sync` |
104
- | `commands/` | Slash commands: `/kata`, `/done`, `/grill`, `/rectify` … |
105
- | `skills/` | On-demand playbooks: `/diagnose`, `/sol-budget`, `/verify-generated-diff` … |
106
- | `bin/` | Hooks and helpers: `git-guardrail.sh`, the `/say` stack |
107
- | `mutation_gate/` | The gate and the `mutation-gate` CLI |
108
-
109
- CI runs `scripts/no-leaks.sh` on every PR. It flags emails, RFC1918
110
- addresses, user-at-host references, `/home/<user>/` paths and non-personal
111
- `ghcr.io/` paths, and prints only `file:line`.
112
-
113
- MIT, see [LICENSE](LICENSE) and [NOTICE.md](NOTICE.md) for one vendored
114
- third-party skill. Contributions: [CONTRIBUTING.md](CONTRIBUTING.md), bound
115
- by [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). Security: [SECURITY.md](SECURITY.md).
@@ -1,26 +0,0 @@
1
- import importlib.util
2
- from pathlib import Path
3
-
4
- MODULE_PATH = Path(__file__).resolve().parents[1] / "bin" / "say-narrate.py"
5
-
6
-
7
- def _load(monkeypatch, plugin_root):
8
- if plugin_root is None:
9
- monkeypatch.delenv("CLAUDE_PLUGIN_ROOT", raising=False)
10
- else:
11
- monkeypatch.setenv("CLAUDE_PLUGIN_ROOT", plugin_root)
12
- spec = importlib.util.spec_from_file_location("say_narrate_probe", MODULE_PATH)
13
- module = importlib.util.module_from_spec(spec)
14
- spec.loader.exec_module(module)
15
- return module
16
-
17
-
18
- def test_bin_resolves_under_plugin_root_when_set(monkeypatch):
19
- module = _load(monkeypatch, "/fake/plugin/root")
20
- assert module.BIN == "/fake/plugin/root/bin"
21
-
22
-
23
- def test_bin_falls_back_to_legacy_claude_bin_when_unset(monkeypatch, tmp_path):
24
- monkeypatch.setenv("HOME", str(tmp_path))
25
- module = _load(monkeypatch, None)
26
- assert module.BIN == str(tmp_path / ".claude" / "bin")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes