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.
- package/CHANGELOG.md +126 -0
- package/COMPATIBILITY.md +13 -0
- package/KNOWN_LIMITATIONS.md +19 -0
- package/LICENSE +44 -0
- package/LICENSING.md +10 -0
- package/README.md +378 -210
- package/SECURITY.md +52 -0
- package/dist/index.d.ts +1477 -0
- package/dist/index.js +3464 -0
- package/dist/index.js.map +1 -0
- package/native/Cargo.lock +4863 -0
- package/native/Cargo.toml +73 -0
- package/native/crates/canonical/Cargo.toml +24 -0
- package/native/crates/canonical/src/lib.rs +673 -0
- package/native/crates/compression/Cargo.toml +20 -0
- package/native/crates/compression/benches/compression_bench.rs +42 -0
- package/native/crates/compression/src/lib.rs +393 -0
- package/native/crates/evm-eth/Cargo.toml +13 -0
- package/native/crates/evm-eth/src/lib.rs +105 -0
- package/native/crates/fee/Cargo.toml +15 -0
- package/native/crates/fee/src/lib.rs +281 -0
- package/native/crates/index/Cargo.toml +16 -0
- package/native/crates/index/src/lib.rs +277 -0
- package/native/crates/policy/Cargo.toml +17 -0
- package/native/crates/policy/src/lib.rs +614 -0
- package/native/crates/security/Cargo.toml +22 -0
- package/native/crates/security/src/lib.rs +478 -0
- package/native/crates/tokens/Cargo.toml +13 -0
- package/native/crates/tokens/src/lib.rs +534 -0
- package/native/crates/verification/Cargo.toml +23 -0
- package/native/crates/verification/src/lib.rs +333 -0
- package/native/crates/wallet/Cargo.toml +20 -0
- package/native/crates/wallet/src/lib.rs +261 -0
- package/native/crates/x402/Cargo.toml +30 -0
- package/native/crates/x402/src/lib.rs +423 -0
- package/native/ffi/Cargo.toml +34 -0
- package/native/ffi/build.rs +4 -0
- package/native/ffi/index.node +0 -0
- package/native/ffi/src/lib.rs +352 -0
- package/native/ffi/tests/integration.rs +354 -0
- package/native/pyo3/Cargo.toml +26 -0
- package/native/pyo3/pyproject.toml +16 -0
- package/native/pyo3/src/lib.rs +407 -0
- package/native/pyo3/tests/test_smoke.py +180 -0
- package/native/wasm/Cargo.toml +44 -0
- package/native/wasm/pkg/.gitignore +6 -0
- package/native/wasm/pkg/clawpowers_wasm.d.ts +208 -0
- package/native/wasm/pkg/clawpowers_wasm.js +872 -0
- package/native/wasm/pkg/clawpowers_wasm_bg.wasm +0 -0
- package/native/wasm/pkg/clawpowers_wasm_bg.wasm.d.ts +40 -0
- package/native/wasm/pkg/package.json +17 -0
- package/native/wasm/pkg-node/.gitignore +6 -0
- package/native/wasm/pkg-node/clawpowers_wasm.d.ts +143 -0
- package/native/wasm/pkg-node/clawpowers_wasm.js +798 -0
- package/native/wasm/pkg-node/clawpowers_wasm_bg.wasm +0 -0
- package/native/wasm/pkg-node/clawpowers_wasm_bg.wasm.d.ts +40 -0
- package/native/wasm/pkg-node/package.json +13 -0
- package/native/wasm/src/lib.rs +433 -0
- package/package.json +71 -44
- package/src/skills/catalog.ts +435 -0
- package/src/skills/executor.ts +56 -0
- package/src/skills/index.ts +3 -0
- package/src/skills/itp/SKILL.md +112 -0
- package/src/skills/loader.ts +193 -0
- package/.claude-plugin/manifest.json +0 -19
- package/.codex/INSTALL.md +0 -36
- package/.cursor-plugin/manifest.json +0 -21
- package/.opencode/INSTALL.md +0 -52
- package/ARCHITECTURE.md +0 -69
- package/bin/clawpowers.js +0 -625
- package/bin/clawpowers.sh +0 -91
- package/docs/demo/clawpowers-demo.cast +0 -197
- package/docs/demo/clawpowers-demo.gif +0 -0
- package/docs/launch-images/25-skills-breakdown.jpg +0 -0
- package/docs/launch-images/clawpowers-vs-superpowers.jpg +0 -0
- package/docs/launch-images/economic-code-optimization.jpg +0 -0
- package/docs/launch-images/native-vs-bridge-2.jpg +0 -0
- package/docs/launch-images/native-vs-bridge.jpg +0 -0
- package/docs/launch-images/post1-hero-lobster.jpg +0 -0
- package/docs/launch-images/post2-dashboard.jpg +0 -0
- package/docs/launch-images/post3-superpowers.jpg +0 -0
- package/docs/launch-images/post4-before-after.jpg +0 -0
- package/docs/launch-images/post5-install-now.jpg +0 -0
- package/docs/launch-images/ultimate-stack.jpg +0 -0
- package/docs/launch-posts.md +0 -76
- package/docs/quickstart-first-transaction.md +0 -204
- package/gemini-extension.json +0 -32
- package/hooks/session-start +0 -205
- package/hooks/session-start.cmd +0 -43
- package/hooks/session-start.js +0 -163
- package/runtime/demo/README.md +0 -78
- package/runtime/demo/x402-mock-server.js +0 -230
- package/runtime/feedback/analyze.js +0 -621
- package/runtime/feedback/analyze.sh +0 -546
- package/runtime/init.js +0 -210
- package/runtime/init.sh +0 -178
- package/runtime/metrics/collector.js +0 -361
- package/runtime/metrics/collector.sh +0 -308
- package/runtime/payments/ledger.js +0 -305
- package/runtime/payments/ledger.sh +0 -262
- package/runtime/payments/pipeline.js +0 -455
- package/runtime/persistence/store.js +0 -433
- package/runtime/persistence/store.sh +0 -303
- package/skill.json +0 -106
- package/skills/agent-bounties/SKILL.md +0 -553
- package/skills/agent-payments/SKILL.md +0 -479
- package/skills/brainstorming/SKILL.md +0 -233
- package/skills/content-pipeline/SKILL.md +0 -282
- package/skills/cross-project-knowledge/SKILL.md +0 -345
- package/skills/dispatching-parallel-agents/SKILL.md +0 -305
- package/skills/economic-code-optimization/SKILL.md +0 -265
- package/skills/executing-plans/SKILL.md +0 -255
- package/skills/finishing-a-development-branch/SKILL.md +0 -260
- package/skills/formal-verification-lite/SKILL.md +0 -441
- package/skills/learn-how-to-learn/SKILL.md +0 -235
- package/skills/market-intelligence/SKILL.md +0 -323
- package/skills/meta-skill-evolution/SKILL.md +0 -325
- package/skills/prospecting/SKILL.md +0 -454
- package/skills/receiving-code-review/SKILL.md +0 -225
- package/skills/requesting-code-review/SKILL.md +0 -206
- package/skills/security-audit/SKILL.md +0 -353
- package/skills/self-healing-code/SKILL.md +0 -369
- package/skills/subagent-driven-development/SKILL.md +0 -244
- package/skills/systematic-debugging/SKILL.md +0 -355
- package/skills/test-driven-development/SKILL.md +0 -416
- package/skills/using-clawpowers/SKILL.md +0 -160
- package/skills/using-git-worktrees/SKILL.md +0 -261
- package/skills/validator/SKILL.md +0 -281
- package/skills/verification-before-completion/SKILL.md +0 -254
- package/skills/writing-plans/SKILL.md +0 -276
- 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 |
|