clawpowers 1.1.4 → 2.2.0

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 (131) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/COMPATIBILITY.md +13 -0
  3. package/KNOWN_LIMITATIONS.md +19 -0
  4. package/LICENSE +44 -0
  5. package/LICENSING.md +10 -0
  6. package/README.md +378 -210
  7. package/SECURITY.md +52 -0
  8. package/dist/index.d.ts +1477 -0
  9. package/dist/index.js +3464 -0
  10. package/dist/index.js.map +1 -0
  11. package/native/Cargo.lock +4863 -0
  12. package/native/Cargo.toml +73 -0
  13. package/native/crates/canonical/Cargo.toml +24 -0
  14. package/native/crates/canonical/src/lib.rs +673 -0
  15. package/native/crates/compression/Cargo.toml +20 -0
  16. package/native/crates/compression/benches/compression_bench.rs +42 -0
  17. package/native/crates/compression/src/lib.rs +393 -0
  18. package/native/crates/evm-eth/Cargo.toml +13 -0
  19. package/native/crates/evm-eth/src/lib.rs +105 -0
  20. package/native/crates/fee/Cargo.toml +15 -0
  21. package/native/crates/fee/src/lib.rs +281 -0
  22. package/native/crates/index/Cargo.toml +16 -0
  23. package/native/crates/index/src/lib.rs +277 -0
  24. package/native/crates/policy/Cargo.toml +17 -0
  25. package/native/crates/policy/src/lib.rs +614 -0
  26. package/native/crates/security/Cargo.toml +22 -0
  27. package/native/crates/security/src/lib.rs +478 -0
  28. package/native/crates/tokens/Cargo.toml +13 -0
  29. package/native/crates/tokens/src/lib.rs +534 -0
  30. package/native/crates/verification/Cargo.toml +23 -0
  31. package/native/crates/verification/src/lib.rs +333 -0
  32. package/native/crates/wallet/Cargo.toml +20 -0
  33. package/native/crates/wallet/src/lib.rs +261 -0
  34. package/native/crates/x402/Cargo.toml +30 -0
  35. package/native/crates/x402/src/lib.rs +423 -0
  36. package/native/ffi/Cargo.toml +34 -0
  37. package/native/ffi/build.rs +4 -0
  38. package/native/ffi/index.node +0 -0
  39. package/native/ffi/src/lib.rs +352 -0
  40. package/native/ffi/tests/integration.rs +354 -0
  41. package/native/pyo3/Cargo.toml +26 -0
  42. package/native/pyo3/pyproject.toml +16 -0
  43. package/native/pyo3/src/lib.rs +407 -0
  44. package/native/pyo3/tests/test_smoke.py +180 -0
  45. package/native/wasm/Cargo.toml +44 -0
  46. package/native/wasm/pkg/.gitignore +6 -0
  47. package/native/wasm/pkg/clawpowers_wasm.d.ts +208 -0
  48. package/native/wasm/pkg/clawpowers_wasm.js +872 -0
  49. package/native/wasm/pkg/clawpowers_wasm_bg.wasm +0 -0
  50. package/native/wasm/pkg/clawpowers_wasm_bg.wasm.d.ts +40 -0
  51. package/native/wasm/pkg/package.json +17 -0
  52. package/native/wasm/pkg-node/.gitignore +6 -0
  53. package/native/wasm/pkg-node/clawpowers_wasm.d.ts +143 -0
  54. package/native/wasm/pkg-node/clawpowers_wasm.js +798 -0
  55. package/native/wasm/pkg-node/clawpowers_wasm_bg.wasm +0 -0
  56. package/native/wasm/pkg-node/clawpowers_wasm_bg.wasm.d.ts +40 -0
  57. package/native/wasm/pkg-node/package.json +13 -0
  58. package/native/wasm/src/lib.rs +433 -0
  59. package/package.json +71 -44
  60. package/src/skills/catalog.ts +435 -0
  61. package/src/skills/executor.ts +56 -0
  62. package/src/skills/index.ts +3 -0
  63. package/src/skills/itp/SKILL.md +112 -0
  64. package/src/skills/loader.ts +193 -0
  65. package/.claude-plugin/manifest.json +0 -19
  66. package/.codex/INSTALL.md +0 -36
  67. package/.cursor-plugin/manifest.json +0 -21
  68. package/.opencode/INSTALL.md +0 -52
  69. package/ARCHITECTURE.md +0 -69
  70. package/bin/clawpowers.js +0 -625
  71. package/bin/clawpowers.sh +0 -91
  72. package/docs/demo/clawpowers-demo.cast +0 -197
  73. package/docs/demo/clawpowers-demo.gif +0 -0
  74. package/docs/launch-images/25-skills-breakdown.jpg +0 -0
  75. package/docs/launch-images/clawpowers-vs-superpowers.jpg +0 -0
  76. package/docs/launch-images/economic-code-optimization.jpg +0 -0
  77. package/docs/launch-images/native-vs-bridge-2.jpg +0 -0
  78. package/docs/launch-images/native-vs-bridge.jpg +0 -0
  79. package/docs/launch-images/post1-hero-lobster.jpg +0 -0
  80. package/docs/launch-images/post2-dashboard.jpg +0 -0
  81. package/docs/launch-images/post3-superpowers.jpg +0 -0
  82. package/docs/launch-images/post4-before-after.jpg +0 -0
  83. package/docs/launch-images/post5-install-now.jpg +0 -0
  84. package/docs/launch-images/ultimate-stack.jpg +0 -0
  85. package/docs/launch-posts.md +0 -76
  86. package/docs/quickstart-first-transaction.md +0 -204
  87. package/gemini-extension.json +0 -32
  88. package/hooks/session-start +0 -205
  89. package/hooks/session-start.cmd +0 -43
  90. package/hooks/session-start.js +0 -163
  91. package/runtime/demo/README.md +0 -78
  92. package/runtime/demo/x402-mock-server.js +0 -230
  93. package/runtime/feedback/analyze.js +0 -621
  94. package/runtime/feedback/analyze.sh +0 -546
  95. package/runtime/init.js +0 -210
  96. package/runtime/init.sh +0 -178
  97. package/runtime/metrics/collector.js +0 -361
  98. package/runtime/metrics/collector.sh +0 -308
  99. package/runtime/payments/ledger.js +0 -305
  100. package/runtime/payments/ledger.sh +0 -262
  101. package/runtime/payments/pipeline.js +0 -455
  102. package/runtime/persistence/store.js +0 -433
  103. package/runtime/persistence/store.sh +0 -303
  104. package/skill.json +0 -106
  105. package/skills/agent-bounties/SKILL.md +0 -553
  106. package/skills/agent-payments/SKILL.md +0 -479
  107. package/skills/brainstorming/SKILL.md +0 -233
  108. package/skills/content-pipeline/SKILL.md +0 -282
  109. package/skills/cross-project-knowledge/SKILL.md +0 -345
  110. package/skills/dispatching-parallel-agents/SKILL.md +0 -305
  111. package/skills/economic-code-optimization/SKILL.md +0 -265
  112. package/skills/executing-plans/SKILL.md +0 -255
  113. package/skills/finishing-a-development-branch/SKILL.md +0 -260
  114. package/skills/formal-verification-lite/SKILL.md +0 -441
  115. package/skills/learn-how-to-learn/SKILL.md +0 -235
  116. package/skills/market-intelligence/SKILL.md +0 -323
  117. package/skills/meta-skill-evolution/SKILL.md +0 -325
  118. package/skills/prospecting/SKILL.md +0 -454
  119. package/skills/receiving-code-review/SKILL.md +0 -225
  120. package/skills/requesting-code-review/SKILL.md +0 -206
  121. package/skills/security-audit/SKILL.md +0 -353
  122. package/skills/self-healing-code/SKILL.md +0 -369
  123. package/skills/subagent-driven-development/SKILL.md +0 -244
  124. package/skills/systematic-debugging/SKILL.md +0 -355
  125. package/skills/test-driven-development/SKILL.md +0 -416
  126. package/skills/using-clawpowers/SKILL.md +0 -160
  127. package/skills/using-git-worktrees/SKILL.md +0 -261
  128. package/skills/validator/SKILL.md +0 -281
  129. package/skills/verification-before-completion/SKILL.md +0 -254
  130. package/skills/writing-plans/SKILL.md +0 -276
  131. package/skills/writing-skills/SKILL.md +0 -260
@@ -1,260 +0,0 @@
1
- ---
2
- name: finishing-a-development-branch
3
- description: Clean up a completed feature branch, write changelog entry, optimize commit history, and prepare for merge. Activate when feature work is done and verified.
4
- version: 1.0.0
5
- requires:
6
- tools: [git, bash]
7
- runtime: false
8
- metrics:
9
- tracks: [squash_accuracy, changelog_quality, merge_conflicts, review_cycles_after_finish]
10
- improves: [commit_message_quality, squash_strategy, changelog_format]
11
- ---
12
-
13
- # Finishing a Development Branch
14
-
15
- ## When to Use
16
-
17
- Apply this skill after:
18
-
19
- - `verification-before-completion` passes all gates
20
- - The feature is functionally complete
21
- - You're ready to open a PR or merge to main
22
-
23
- **Don't start this skill until verification passes.** Finishing a bad branch just makes it a clean bad branch.
24
-
25
- ## Core Methodology
26
-
27
- ### Step 1: Final State Verification
28
-
29
- Before touching the branch:
30
-
31
- ```bash
32
- git status # Clean working tree (nothing unstaged)
33
- git log --oneline main..HEAD # All your commits, review them
34
- git diff main...HEAD # Full diff against main
35
- ```
36
-
37
- If there are uncommitted changes: commit them or stash them. If there are WIP commits ("temp", "wip", "debugging"), they need to be cleaned up in Step 3.
38
-
39
- ### Step 2: Changelog Entry
40
-
41
- Write the changelog before squashing. You have the full commit history available now — use it.
42
-
43
- **Format:** Follow [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) conventions:
44
-
45
- ```markdown
46
- ## [Unreleased]
47
-
48
- ### Added
49
- - JWT-based authentication with RS256 signing (`auth.issue()`, `auth.validate()`)
50
- - Session management with configurable TTL
51
- - User registration endpoint with email verification flow
52
-
53
- ### Changed
54
- - `UserService.create()` now requires email verification before login is permitted
55
-
56
- ### Fixed
57
- - Connection pool exhaustion under concurrent load (was not releasing connections in error paths)
58
-
59
- ### Security
60
- - Added bcrypt password hashing (cost factor 12)
61
- - Rate limiting on auth endpoints (10 req/min per IP)
62
- ```
63
-
64
- **Categories:**
65
- - `Added` — New features
66
- - `Changed` — Changes to existing functionality
67
- - `Deprecated` — Soon-to-be-removed features
68
- - `Removed` — Removed features
69
- - `Fixed` — Bug fixes
70
- - `Security` — Security-related changes
71
-
72
- **Rules:**
73
- - Write for humans, not for git log readers
74
- - Link to issue numbers if they exist: `(#123)`
75
- - Be specific about what changed, not how it changed
76
- - One changelog entry per PR, not per commit
77
-
78
- ### Step 3: Commit History Optimization
79
-
80
- Review all commits between your branch and main:
81
-
82
- ```bash
83
- git log --oneline main..HEAD
84
- ```
85
-
86
- **Squash strategy:**
87
-
88
- | Commit pattern | Action |
89
- |---------------|--------|
90
- | `wip`, `temp`, `debug` commits | Squash into parent |
91
- | Multiple tiny commits for same logical change | Squash into one |
92
- | Fix commits for mistakes in the same PR | Squash into the commit being fixed |
93
- | Logical, independent changes | Keep separate |
94
- | Each commit is one testable unit | Keep as-is |
95
-
96
- **Interactive rebase:**
97
- ```bash
98
- git rebase -i main
99
- # Opens editor — mark commits as:
100
- # pick: keep as-is
101
- # squash (s): merge into previous commit
102
- # fixup (f): merge into previous commit, discard this commit's message
103
- # reword (r): keep but edit the message
104
- ```
105
-
106
- **After squash, verify:**
107
- ```bash
108
- git log --oneline main..HEAD # Should show clean, logical commits
109
- git diff main...HEAD # Diff should be identical to before squash
110
- ```
111
-
112
- ### Step 4: Conventional Commit Messages
113
-
114
- Each remaining commit must use Conventional Commits format:
115
-
116
- ```
117
- <type>[optional scope]: <description>
118
-
119
- [optional body]
120
-
121
- [optional footer(s)]
122
- ```
123
-
124
- **Types:**
125
- - `feat`: New feature (triggers MINOR version bump)
126
- - `fix`: Bug fix (triggers PATCH version bump)
127
- - `perf`: Performance improvement
128
- - `refactor`: Code restructuring (no behavior change)
129
- - `test`: Adding or fixing tests
130
- - `docs`: Documentation only
131
- - `ci`: CI/CD configuration
132
- - `chore`: Dependency updates, tooling
133
-
134
- **Breaking changes:** Add `!` after type or `BREAKING CHANGE:` footer
135
- ```
136
- feat!: remove deprecated V1 auth endpoints
137
-
138
- BREAKING CHANGE: /api/v1/login and /api/v1/logout have been removed.
139
- Use /api/v2/auth/login and /api/v2/auth/logout instead.
140
- ```
141
-
142
- **Examples:**
143
- ```
144
- feat(auth): implement JWT authentication with RS256 signing
145
- fix(auth): release connection in error path of process_payment
146
- perf(cache): add Redis-backed session cache for hot paths
147
- ```
148
-
149
- ### Step 5: Branch Cleanup Check
150
-
151
- ```bash
152
- # Verify the branch builds and tests pass after rebase
153
- git checkout feature/auth
154
- # [run test suite]
155
- pytest # or npm test or go test ./...
156
-
157
- # Verify clean diff (no accidental deletions)
158
- git diff main...HEAD --stat
159
- ```
160
-
161
- ### Step 6: PR Description
162
-
163
- Write the PR description while everything is fresh:
164
-
165
- ```markdown
166
- ## Summary
167
-
168
- [2-3 sentences: what this PR does and why]
169
-
170
- ## Changes
171
-
172
- - [Key change 1]
173
- - [Key change 2]
174
- - [Key change 3]
175
-
176
- ## Testing
177
-
178
- - [What tests cover this change]
179
- - [Any manual testing done]
180
- - [Edge cases explicitly tested]
181
-
182
- ## Breaking Changes
183
-
184
- [None / description of any breaking changes]
185
-
186
- ## Screenshots / Output
187
-
188
- [If UI or CLI output changed, show before/after]
189
-
190
- ## Checklist
191
-
192
- - [x] Tests pass (127 passing, 0 failing)
193
- - [x] Coverage ≥ 80% (84% for new code)
194
- - [x] No linting errors
195
- - [x] CHANGELOG updated
196
- - [x] Documentation updated
197
- ```
198
-
199
- ### Step 7: Final Push
200
-
201
- ```bash
202
- git push origin feature/auth-service
203
- # If after rebase:
204
- git push origin feature/auth-service --force-with-lease # Never --force alone
205
- ```
206
-
207
- `--force-with-lease` is safe: it rejects the push if the remote branch has changed since your last fetch, preventing overwriting someone else's work.
208
-
209
- ## ClawPowers Enhancement
210
-
211
- When `~/.clawpowers/` runtime is initialized:
212
-
213
- **Automated Squash Strategy:**
214
-
215
- Based on commit history patterns, the framework suggests optimal squash boundaries:
216
- ```bash
217
- bash runtime/persistence/store.sh get "config:branch-finish:squash_strategy"
218
- # → logical_units (keep separate commits per feature unit)
219
- # → single_commit (squash entire branch to one commit for hotfixes)
220
- ```
221
-
222
- **Conventional Commit Enforcement:**
223
-
224
- Before the push step, validate all commit messages:
225
- ```bash
226
- bash runtime/persistence/store.sh get "config:branch-finish:conventional_commits"
227
- # → strict (reject non-conforming messages)
228
- # → warn (flag but allow)
229
- ```
230
-
231
- **Changelog Quality Scoring:**
232
-
233
- Stores past changelog entries and scores them on:
234
- - Specificity (named functions/endpoints vs. vague descriptions)
235
- - Completeness (all changes captured)
236
- - Human readability
237
-
238
- ```bash
239
- bash runtime/metrics/collector.sh record \
240
- --skill finishing-a-development-branch \
241
- --outcome success \
242
- --notes "auth-service: 12 commits → 4, conventional commits enforced, changelog written"
243
- ```
244
-
245
- ## Anti-Patterns
246
-
247
- | Anti-Pattern | Why It Fails | Correct Approach |
248
- |-------------|-------------|-----------------|
249
- | Squashing all commits to one regardless of size | Loses traceability for large PRs | Squash to logical units, not one blob |
250
- | `--force` push (not `--force-with-lease`) | Overwrites teammates' commits silently | Always `--force-with-lease` |
251
- | Vague changelog ("various fixes") | Useless for users and future developers | Specific, named changes with context |
252
- | Writing PR description after review starts | Reviewers lack context | Write description before requesting review |
253
- | Not verifying after rebase | Rebase conflicts silently break behavior | Run full test suite after any rebase |
254
- | WIP commits in merged history | Pollutes git log | Squash WIP commits unconditionally |
255
-
256
- ## Integration with Other Skills
257
-
258
- - Preceded by `verification-before-completion`
259
- - Followed by `requesting-code-review`
260
- - Use `using-git-worktrees` if finishing one of several concurrent branches
@@ -1,441 +0,0 @@
1
- ---
2
- name: formal-verification-lite
3
- description: Goes beyond unit tests with property-based testing. Generate invariant properties for functions, write tests with fast-check/Hypothesis/QuickCheck, run 1000+ examples per property, and track edge cases found. Integrates with TDD after GREEN phase.
4
- version: 1.0.0
5
- requires:
6
- tools: [bash, node]
7
- runtime: false
8
- metrics:
9
- tracks: [properties_discovered, edge_cases_found, false_positive_rate, properties_per_function, shrink_examples_count]
10
- improves: [property_coverage, edge_case_detection_rate, false_positive_threshold]
11
- ---
12
-
13
- # Formal Verification Lite
14
-
15
- ## When to Use
16
-
17
- Apply this skill when:
18
-
19
- - The TDD GREEN phase is complete and you have passing unit tests
20
- - Implementing pure functions with mathematical properties (sort, serialize, parse, encrypt)
21
- - Building data transformation pipelines where correctness matters at scale
22
- - Implementing state machines, parsers, or any function where "all inputs" behavior matters
23
- - A bug report mentions an edge case that unit tests didn't cover
24
- - Before shipping code to production that handles external/untrusted input
25
-
26
- **Skip when:**
27
- - Functions have no invariant properties (e.g., a function that sends an email — pure I/O)
28
- - Tests require real external services (use integration tests instead)
29
- - The function is pure configuration or pure UI rendering
30
- - You're still in RED phase — finish TDD first, then apply this skill
31
-
32
- **Decision tree:**
33
- ```
34
- Does the function have any of these properties?
35
- ├── Roundtrip: encode/decode, serialize/deserialize → apply roundtrip pattern
36
- ├── Idempotence: f(f(x)) == f(x) → apply idempotence pattern
37
- ├── Commutativity: f(a,b) == f(b,a) → apply commutativity pattern
38
- ├── Monotonicity: if a ≤ b then f(a) ≤ f(b) → apply monotone pattern
39
- ├── Length preservation: output.length == input.length → apply structural pattern
40
- └── None of these → unit tests are sufficient; skip this skill
41
- ```
42
-
43
- ## Core Methodology
44
-
45
- ### The Property-Based Testing Mindset
46
-
47
- Unit tests check specific examples: `sort([3,1,2]) === [1,2,3]`.
48
- Property tests check **universal invariants**: `∀ array: sort(array).length === array.length`.
49
-
50
- The difference:
51
- - Unit test: verifies 1 input
52
- - Property test with 1000 iterations: verifies 1000 randomly-generated inputs, including edge cases the developer never thought of
53
-
54
- **The property you write describes what must ALWAYS be true.** The framework finds inputs that break it.
55
-
56
- ### Step 1: Identify Properties
57
-
58
- Before writing code, list the mathematical properties of your function. Use this taxonomy:
59
-
60
- **Roundtrip (parse/serialize inverse pairs):**
61
- ```
62
- parse(serialize(x)) == x
63
- deserialize(serialize(x)) == x
64
- decode(encode(x)) == x
65
- ```
66
-
67
- **Idempotence (applying twice = applying once):**
68
- ```
69
- normalize(normalize(x)) == normalize(x)
70
- deduplicate(deduplicate(x)) == deduplicate(x)
71
- trim(trim(x)) == trim(x)
72
- ```
73
-
74
- **Commutativity (order doesn't matter):**
75
- ```
76
- merge(a, b) == merge(b, a)
77
- add(a, b) == add(b, a)
78
- union(setA, setB) == union(setB, setA)
79
- ```
80
-
81
- **Monotonicity (order-preserving):**
82
- ```
83
- if a <= b then score(a) <= score(b)
84
- if input.length increases then output.length >= previous output.length
85
- ```
86
-
87
- **Structural invariants:**
88
- ```
89
- sort(arr).length == arr.length
90
- sort(arr) contains same elements as arr
91
- filter(arr, pred).every(pred)
92
- map(arr, f).length == arr.length
93
- ```
94
-
95
- **Conservation laws:**
96
- ```
97
- sum(split(total, n)) == total
98
- partition(arr).flatMap(x=>x).length == arr.length
99
- ```
100
-
101
- ### Step 2: Write Property Tests
102
-
103
- #### JavaScript / TypeScript — fast-check
104
-
105
- ```bash
106
- npm install --save-dev fast-check
107
- ```
108
-
109
- ```typescript
110
- import * as fc from 'fast-check';
111
- import { serialize, deserialize } from './serializer';
112
- import { sort } from './sort';
113
- import { merge } from './merge';
114
-
115
- // === Roundtrip property ===
116
- test('serialize/deserialize roundtrip', () => {
117
- fc.assert(
118
- fc.property(
119
- fc.record({ // generate random records
120
- id: fc.uuid(),
121
- name: fc.string({ minLength: 1 }),
122
- age: fc.integer({ min: 0, max: 150 }),
123
- tags: fc.array(fc.string()),
124
- }),
125
- (obj) => {
126
- const result = deserialize(serialize(obj));
127
- expect(result).toEqual(obj); // must roundtrip perfectly
128
- }
129
- ),
130
- { numRuns: 1000 } // run 1000 random examples
131
- );
132
- });
133
-
134
- // === Structural invariant ===
135
- test('sort preserves length and elements', () => {
136
- fc.assert(
137
- fc.property(
138
- fc.array(fc.integer()),
139
- (arr) => {
140
- const sorted = sort(arr);
141
- // Property 1: length preserved
142
- expect(sorted.length).toBe(arr.length);
143
- // Property 2: elements preserved (multiset equality)
144
- expect(sorted.slice().sort()).toEqual(arr.slice().sort());
145
- // Property 3: monotone increasing
146
- for (let i = 1; i < sorted.length; i++) {
147
- expect(sorted[i]).toBeGreaterThanOrEqual(sorted[i-1]);
148
- }
149
- }
150
- ),
151
- { numRuns: 1000 }
152
- );
153
- });
154
-
155
- // === Commutativity ===
156
- test('merge is commutative', () => {
157
- fc.assert(
158
- fc.property(
159
- fc.record({ a: fc.integer(), b: fc.string() }),
160
- fc.record({ a: fc.integer(), b: fc.string() }),
161
- (objA, objB) => {
162
- expect(merge(objA, objB)).toEqual(merge(objB, objA));
163
- }
164
- ),
165
- { numRuns: 500 }
166
- );
167
- });
168
-
169
- // === Idempotence ===
170
- test('normalize is idempotent', () => {
171
- fc.assert(
172
- fc.property(
173
- fc.string(),
174
- (str) => {
175
- const once = normalize(str);
176
- const twice = normalize(normalize(str));
177
- expect(once).toEqual(twice);
178
- }
179
- ),
180
- { numRuns: 1000 }
181
- );
182
- });
183
- ```
184
-
185
- #### Python — Hypothesis
186
-
187
- ```bash
188
- pip install hypothesis
189
- ```
190
-
191
- ```python
192
- from hypothesis import given, settings, strategies as st
193
- from hypothesis import HealthCheck
194
- from mymodule import serialize, deserialize, sort_items, merge_dicts
195
-
196
- # === Roundtrip property ===
197
- @given(st.fixed_dictionaries({
198
- 'id': st.uuids().map(str),
199
- 'name': st.text(min_size=1, max_size=100),
200
- 'age': st.integers(min_value=0, max_value=150),
201
- 'tags': st.lists(st.text()),
202
- }))
203
- @settings(max_examples=1000)
204
- def test_serialize_deserialize_roundtrip(obj):
205
- assert deserialize(serialize(obj)) == obj
206
-
207
- # === Structural invariant ===
208
- @given(st.lists(st.integers()))
209
- @settings(max_examples=1000)
210
- def test_sort_preserves_structure(arr):
211
- sorted_arr = sort_items(arr)
212
- assert len(sorted_arr) == len(arr) # length preserved
213
- assert sorted(sorted_arr) == sorted(arr) # elements preserved
214
- for i in range(1, len(sorted_arr)): # monotone
215
- assert sorted_arr[i] >= sorted_arr[i-1]
216
-
217
- # === Commutativity ===
218
- @given(
219
- st.dictionaries(st.text(), st.integers()),
220
- st.dictionaries(st.text(), st.integers()),
221
- )
222
- @settings(max_examples=500)
223
- def test_merge_commutative(dict_a, dict_b):
224
- assert merge_dicts(dict_a, dict_b) == merge_dicts(dict_b, dict_a)
225
-
226
- # === Conservation law ===
227
- @given(st.integers(min_value=1, max_value=10000), st.integers(min_value=2, max_value=10))
228
- @settings(max_examples=500)
229
- def test_split_sum_conserved(total, n):
230
- parts = split(total, n)
231
- assert sum(parts) == total
232
- assert len(parts) == n
233
- ```
234
-
235
- #### Go — testing/quick or gopter
236
-
237
- ```go
238
- import (
239
- "testing"
240
- "testing/quick"
241
- "reflect"
242
- )
243
-
244
- // Roundtrip property
245
- func TestSerializeRoundtrip(t *testing.T) {
246
- f := func(data Record) bool {
247
- serialized := Serialize(data)
248
- deserialized, err := Deserialize(serialized)
249
- return err == nil && reflect.DeepEqual(data, deserialized)
250
- }
251
- if err := quick.Check(f, &quick.Config{MaxCount: 1000}); err != nil {
252
- t.Error(err)
253
- }
254
- }
255
-
256
- // Sort structural invariant
257
- func TestSortInvariant(t *testing.T) {
258
- f := func(arr []int) bool {
259
- sorted := Sort(append([]int{}, arr...))
260
- if len(sorted) != len(arr) { return false }
261
- for i := 1; i < len(sorted); i++ {
262
- if sorted[i] < sorted[i-1] { return false }
263
- }
264
- return true
265
- }
266
- quick.Check(f, &quick.Config{MaxCount: 1000})
267
- }
268
- ```
269
-
270
- ### Step 3: Run with High Iteration Count
271
-
272
- Default settings in most frameworks are too low (100 examples). Always override:
273
-
274
- ```bash
275
- # fast-check: numRuns: 1000+ per property
276
- # Hypothesis: max_examples=1000 per test
277
- # QuickCheck: maxSuccess 1000
278
-
279
- # Run with seed for reproducibility on failure
280
- npx jest --testNamePattern="property"
281
- # If a failure occurs, fast-check outputs the seed and minimal counterexample
282
-
283
- # Python Hypothesis with verbose output
284
- pytest --hypothesis-show-statistics tests/test_properties.py
285
- ```
286
-
287
- **When a property fails:**
288
-
289
- fast-check and Hypothesis both **shrink** the counterexample — they find the *smallest* failing input. This is the key advantage over manual testing:
290
-
291
- ```
292
- Property failed after 47 examples:
293
- Counterexample: { id: "", name: "a", age: 0, tags: [] }
294
- ↕ shrunk from: { id: "abc-xyz-...", name: "hello world", age: 42, tags: ["x","y"] }
295
- ```
296
-
297
- The shrunk example directly points to the bug: empty string `id` breaks the serializer.
298
-
299
- ### Step 4: Track Properties and Edge Cases
300
-
301
- After each property-testing run, record what was discovered:
302
-
303
- ```bash
304
- # Record properties found and edge cases surfaced
305
- cat >> ~/.clawpowers/memory/property-log.jsonl <<EOF
306
- {
307
- "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
308
- "function": "$FUNCTION_NAME",
309
- "project": "$(basename $(git rev-parse --show-toplevel))",
310
- "properties_tested": $PROPERTIES_COUNT,
311
- "iterations": $TOTAL_ITERATIONS,
312
- "edge_cases_found": $EDGE_CASES,
313
- "counterexamples": $COUNTEREXAMPLES_JSON,
314
- "false_positives": $FALSE_POSITIVES
315
- }
316
- EOF
317
- ```
318
-
319
- **False positive tracking:** If a property test fails because the property was wrong (not the implementation), that's a false positive. Track these — high false positive rate means your properties need tightening.
320
-
321
- ### Step 5: Integration with TDD
322
-
323
- After the GREEN phase, add property tests before entering REFACTOR:
324
-
325
- ```
326
- RED → GREEN → [formal-verification-lite] → REFACTOR
327
- ```
328
-
329
- **Integration flow:**
330
- 1. TDD GREEN: unit tests pass for specific examples
331
- 2. formal-verification-lite: identify properties → write property tests → run 1000 iterations
332
- 3. If property tests surface a bug: go back to GREEN to fix
333
- 4. If all property tests pass: enter REFACTOR with confidence that behavior is correct for all inputs
334
-
335
- ```python
336
- # After unit tests pass:
337
- # test_auth.py — unit tests (specific examples)
338
- def test_jwt_issue_returns_token():
339
- auth = AuthService(secret="test")
340
- result = auth.issue("u123", 3600)
341
- assert result["token"] is not None
342
-
343
- # test_auth_properties.py — property tests (universal)
344
- @given(
345
- user_id=st.text(min_size=1, max_size=100),
346
- ttl=st.integers(min_value=1, max_value=86400)
347
- )
348
- @settings(max_examples=500)
349
- def test_jwt_roundtrip(user_id, ttl):
350
- auth = AuthService(secret="test")
351
- token_data = auth.issue(user_id, ttl)
352
- validated = auth.validate(token_data["token"])
353
- assert validated["user_id"] == user_id
354
- assert validated["valid"] == True
355
- ```
356
-
357
- ### Common Property Templates
358
-
359
- ```typescript
360
- // Template 1: Roundtrip
361
- fc.assert(fc.property(arbitraryInput, (x) => {
362
- expect(decode(encode(x))).toEqual(x);
363
- }), { numRuns: 1000 });
364
-
365
- // Template 2: Idempotence
366
- fc.assert(fc.property(arbitraryInput, (x) => {
367
- expect(f(f(x))).toEqual(f(x));
368
- }), { numRuns: 1000 });
369
-
370
- // Template 3: Commutativity
371
- fc.assert(fc.property(arbitraryA, arbitraryB, (a, b) => {
372
- expect(f(a, b)).toEqual(f(b, a));
373
- }), { numRuns: 500 });
374
-
375
- // Template 4: Monotonicity
376
- fc.assert(fc.property(fc.tuple(arbitraryNum, arbitraryNum), ([a, b]) => {
377
- fc.pre(a <= b); // pre-condition
378
- expect(score(a)).toBeLessThanOrEqual(score(b));
379
- }), { numRuns: 500 });
380
-
381
- // Template 5: Conservation
382
- fc.assert(fc.property(arbitraryArray, (arr) => {
383
- const parts = partition(arr);
384
- expect(parts.flat()).toEqual(expect.arrayContaining(arr));
385
- expect(parts.flat().length).toBe(arr.length);
386
- }), { numRuns: 1000 });
387
- ```
388
-
389
- ## ClawPowers Enhancement
390
-
391
- When `~/.clawpowers/` runtime is initialized:
392
-
393
- **Track property discovery over time:**
394
-
395
- ```bash
396
- bash runtime/persistence/store.sh set "fvl:$PROJECT:$FUNCTION:properties_count" "$PROPERTIES_COUNT"
397
- bash runtime/persistence/store.sh set "fvl:$PROJECT:$FUNCTION:edge_cases_found" "$EDGE_CASES"
398
- bash runtime/persistence/store.sh set "fvl:$PROJECT:last_run" "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
399
- ```
400
-
401
- **Metrics recording:**
402
-
403
- ```bash
404
- bash runtime/metrics/collector.sh record \
405
- --skill formal-verification-lite \
406
- --outcome success \
407
- --notes "function: $FUNCTION_NAME, properties: $PROPERTIES_COUNT, iterations: $TOTAL_ITERATIONS, edge cases: $EDGE_CASES"
408
- ```
409
-
410
- **Analyze property-testing effectiveness:**
411
-
412
- ```bash
413
- bash runtime/feedback/analyze.sh --filter formal-verification-lite
414
- # Reports: edge cases found per 1000 iterations, functions with most property failures,
415
- # false positive rate trend, which property patterns are most productive
416
- ```
417
-
418
- **Cross-project property library:**
419
- ```bash
420
- # Store a reusable property template in knowledge base
421
- search_patterns "roundtrip property" "testing"
422
- store_pattern "testing" \
423
- "Roundtrip property for JSON-serializable data structures" \
424
- "Any function pair encode/decode or serialize/deserialize" \
425
- "Property: decode(encode(x)) == x with 1000 iterations" \
426
- "fc.assert(fc.property(fc.jsonValue(), x => expect(decode(encode(x))).toEqual(x)), {numRuns:1000})" \
427
- "property-based,roundtrip,fast-check,hypothesis"
428
- ```
429
-
430
- ## Anti-Patterns
431
-
432
- | Anti-Pattern | Why It Fails | Correct Approach |
433
- |-------------|-------------|-----------------|
434
- | Run with default iteration count (< 100) | Edge cases aren't found; same as unit tests | Always set numRuns/max_examples ≥ 1000 |
435
- | Write tautological properties | Property always passes, catches nothing | `expect(sort(arr).length >= 0)` is useless; test real invariants |
436
- | Use property tests instead of unit tests | Harder to debug specific examples | Use both: unit tests for known examples, property tests for invariants |
437
- | Skip shrinking | Large counterexamples are hard to debug | Let the framework shrink; always look at the minimal counterexample |
438
- | Write properties before GREEN phase | Tests fail for wrong reasons | Complete TDD GREEN first, then add property tests |
439
- | Test implementation details in properties | Properties break on refactor | Test mathematical relationships, not internal state |
440
- | High false positive rate (> 10%) | Wastes time on wrong property definitions | Tighten pre-conditions with `fc.pre()` or `assume()` |
441
- | Apply to I/O-heavy functions | Property tests of side effects are flaky | Property tests are for pure functions only |