invar-tools 1.8.0__py3-none-any.whl → 1.11.0__py3-none-any.whl
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.
- invar/__init__.py +8 -0
- invar/core/doc_edit.py +187 -0
- invar/core/doc_parser.py +563 -0
- invar/core/language.py +88 -0
- invar/core/models.py +106 -0
- invar/core/patterns/detector.py +6 -1
- invar/core/patterns/p0_exhaustive.py +15 -3
- invar/core/patterns/p0_literal.py +15 -3
- invar/core/patterns/p0_newtype.py +15 -3
- invar/core/patterns/p0_nonempty.py +15 -3
- invar/core/patterns/p0_validation.py +15 -3
- invar/core/patterns/registry.py +5 -1
- invar/core/patterns/types.py +5 -1
- invar/core/property_gen.py +4 -0
- invar/core/rules.py +84 -18
- invar/core/sync_helpers.py +27 -1
- invar/core/ts_parsers.py +286 -0
- invar/core/ts_sig_parser.py +310 -0
- invar/mcp/handlers.py +408 -0
- invar/mcp/server.py +288 -143
- invar/node_tools/MANIFEST +7 -0
- invar/node_tools/__init__.py +51 -0
- invar/node_tools/fc-runner/cli.js +77 -0
- invar/node_tools/quick-check/cli.js +28 -0
- invar/node_tools/ts-analyzer/cli.js +480 -0
- invar/shell/claude_hooks.py +35 -12
- invar/shell/commands/doc.py +409 -0
- invar/shell/commands/guard.py +41 -1
- invar/shell/commands/init.py +154 -16
- invar/shell/commands/perception.py +157 -33
- invar/shell/commands/skill.py +187 -0
- invar/shell/commands/template_sync.py +65 -13
- invar/shell/commands/uninstall.py +60 -12
- invar/shell/commands/update.py +6 -14
- invar/shell/contract_coverage.py +1 -0
- invar/shell/doc_tools.py +459 -0
- invar/shell/fs.py +67 -13
- invar/shell/pi_hooks.py +6 -0
- invar/shell/prove/crosshair.py +3 -0
- invar/shell/prove/guard_ts.py +902 -0
- invar/shell/skill_manager.py +355 -0
- invar/shell/template_engine.py +28 -4
- invar/shell/templates.py +4 -4
- invar/templates/claude-md/python/critical-rules.md +33 -0
- invar/templates/claude-md/python/quick-reference.md +24 -0
- invar/templates/claude-md/typescript/critical-rules.md +40 -0
- invar/templates/claude-md/typescript/quick-reference.md +24 -0
- invar/templates/claude-md/universal/check-in.md +25 -0
- invar/templates/claude-md/universal/skills.md +73 -0
- invar/templates/claude-md/universal/workflow.md +55 -0
- invar/templates/commands/{audit.md → audit.md.jinja} +18 -1
- invar/templates/config/AGENT.md.jinja +58 -0
- invar/templates/config/CLAUDE.md.jinja +16 -209
- invar/templates/config/context.md.jinja +19 -0
- invar/templates/examples/{README.md → python/README.md} +2 -0
- invar/templates/examples/{conftest.py → python/conftest.py} +1 -1
- invar/templates/examples/{contracts.py → python/contracts.py} +81 -4
- invar/templates/examples/python/core_shell.py +227 -0
- invar/templates/examples/python/functional.py +613 -0
- invar/templates/examples/typescript/README.md +31 -0
- invar/templates/examples/typescript/contracts.ts +163 -0
- invar/templates/examples/typescript/core_shell.ts +374 -0
- invar/templates/examples/typescript/functional.ts +601 -0
- invar/templates/examples/typescript/workflow.md +95 -0
- invar/templates/hooks/PostToolUse.sh.jinja +10 -1
- invar/templates/hooks/PreToolUse.sh.jinja +38 -0
- invar/templates/hooks/Stop.sh.jinja +1 -1
- invar/templates/hooks/UserPromptSubmit.sh.jinja +7 -0
- invar/templates/hooks/pi/invar.ts.jinja +9 -0
- invar/templates/manifest.toml +7 -6
- invar/templates/onboard/assessment.md.jinja +214 -0
- invar/templates/onboard/patterns/python.md +347 -0
- invar/templates/onboard/patterns/typescript.md +452 -0
- invar/templates/onboard/roadmap.md.jinja +168 -0
- invar/templates/protocol/INVAR.md.jinja +51 -0
- invar/templates/protocol/python/architecture-examples.md +41 -0
- invar/templates/protocol/python/contracts-syntax.md +56 -0
- invar/templates/protocol/python/markers.md +44 -0
- invar/templates/protocol/python/tools.md +24 -0
- invar/templates/protocol/python/troubleshooting.md +38 -0
- invar/templates/protocol/typescript/architecture-examples.md +52 -0
- invar/templates/protocol/typescript/contracts-syntax.md +73 -0
- invar/templates/protocol/typescript/markers.md +48 -0
- invar/templates/protocol/typescript/tools.md +65 -0
- invar/templates/protocol/typescript/troubleshooting.md +104 -0
- invar/templates/protocol/universal/architecture.md +36 -0
- invar/templates/protocol/universal/completion.md +14 -0
- invar/templates/protocol/universal/contracts-concept.md +37 -0
- invar/templates/protocol/universal/header.md +17 -0
- invar/templates/protocol/universal/session.md +17 -0
- invar/templates/protocol/universal/six-laws.md +10 -0
- invar/templates/protocol/universal/usbv.md +14 -0
- invar/templates/protocol/universal/visible-workflow.md +25 -0
- invar/templates/skills/develop/SKILL.md.jinja +85 -3
- invar/templates/skills/extensions/_registry.yaml +93 -0
- invar/templates/skills/extensions/acceptance/SKILL.md +383 -0
- invar/templates/skills/extensions/invar-onboard/SKILL.md +448 -0
- invar/templates/skills/extensions/invar-onboard/patterns/python.md +347 -0
- invar/templates/skills/extensions/invar-onboard/patterns/typescript.md +452 -0
- invar/templates/skills/extensions/invar-onboard/templates/assessment.md.jinja +214 -0
- invar/templates/skills/extensions/invar-onboard/templates/roadmap.md.jinja +168 -0
- invar/templates/skills/extensions/security/SKILL.md +382 -0
- invar/templates/skills/extensions/security/patterns/_common.yaml +126 -0
- invar/templates/skills/extensions/security/patterns/python.yaml +155 -0
- invar/templates/skills/extensions/security/patterns/typescript.yaml +194 -0
- invar/templates/skills/review/SKILL.md.jinja +220 -248
- {invar_tools-1.8.0.dist-info → invar_tools-1.11.0.dist-info}/METADATA +336 -12
- invar_tools-1.11.0.dist-info/RECORD +178 -0
- invar/templates/examples/core_shell.py +0 -127
- invar/templates/protocol/INVAR.md +0 -310
- invar_tools-1.8.0.dist-info/RECORD +0 -116
- /invar/templates/examples/{workflow.md → python/workflow.md} +0 -0
- {invar_tools-1.8.0.dist-info → invar_tools-1.11.0.dist-info}/WHEEL +0 -0
- {invar_tools-1.8.0.dist-info → invar_tools-1.11.0.dist-info}/entry_points.txt +0 -0
- {invar_tools-1.8.0.dist-info → invar_tools-1.11.0.dist-info}/licenses/LICENSE +0 -0
- {invar_tools-1.8.0.dist-info → invar_tools-1.11.0.dist-info}/licenses/LICENSE-GPL +0 -0
- {invar_tools-1.8.0.dist-info → invar_tools-1.11.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
## Documentation Structure
|
|
2
|
+
|
|
3
|
+
| File | Owner | Edit? | Purpose |
|
|
4
|
+
|------|-------|-------|---------|
|
|
5
|
+
| INVAR.md | Invar | No | Protocol (`invar update` to sync) |
|
|
6
|
+
| CLAUDE.md | User | Yes | Project customization (this file) |
|
|
7
|
+
| .invar/context.md | User | Yes | Project state, lessons learned |
|
|
8
|
+
| .invar/project-additions.md | User | Yes | Project rules → injected into CLAUDE.md |
|
|
9
|
+
| .invar/examples/ | Invar | No | **Must read:** Core/Shell patterns, workflow |
|
|
10
|
+
|
|
11
|
+
> **Before writing code:** Check Task Router in `.invar/context.md`
|
|
12
|
+
|
|
13
|
+
## Visible Workflow (DX-30)
|
|
14
|
+
|
|
15
|
+
For complex tasks (3+ functions), show 3 checkpoints in TodoList:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
□ [UNDERSTAND] Task description, codebase context, constraints
|
|
19
|
+
□ [SPECIFY] Contracts and design decomposition
|
|
20
|
+
□ [VALIDATE] Guard results, Review Gate status, integration status
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**BUILD is internal work** — not shown in TodoList.
|
|
24
|
+
|
|
25
|
+
**Show contracts before code.** See `.invar/examples/workflow.md` for full example.
|
|
26
|
+
|
|
27
|
+
## Phase Visibility (DX-51)
|
|
28
|
+
|
|
29
|
+
Each USBV phase transition requires a visible header:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
33
|
+
📍 /develop → SPECIFY (2/4)
|
|
34
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Three-layer visibility:**
|
|
38
|
+
- **Skill** (`/develop`) — Routing announcement
|
|
39
|
+
- **Phase** (`SPECIFY 2/4`) — Phase header (this section)
|
|
40
|
+
- **Tasks** — TodoWrite items
|
|
41
|
+
|
|
42
|
+
Phase headers are SEPARATE from TodoWrite. Phase = where you are; TodoWrite = what to do.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Context Management (DX-54)
|
|
47
|
+
|
|
48
|
+
Re-read `.invar/context.md` when:
|
|
49
|
+
1. Entering any workflow (/develop, /review, etc.)
|
|
50
|
+
2. Completing a TodoWrite task (before moving to next)
|
|
51
|
+
3. Conversation exceeds ~15-20 exchanges
|
|
52
|
+
4. Unsure about project rules or patterns
|
|
53
|
+
|
|
54
|
+
**Refresh is transparent** — do not announce "I'm refreshing context."
|
|
55
|
+
Only show routing announcements when entering workflows.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
_invar:
|
|
3
|
-
version: "
|
|
3
|
+
version: "{{ version }}"
|
|
4
4
|
type: command
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -51,12 +51,21 @@ You ARE here to:
|
|
|
51
51
|
> **Principle:** Only items requiring semantic judgment. Mechanical checks are excluded (see bottom).
|
|
52
52
|
|
|
53
53
|
### A. Contract Semantic Value
|
|
54
|
+
{% if language == "python" %}
|
|
54
55
|
- [ ] Does @pre constrain inputs beyond type checking?
|
|
55
56
|
- Bad: `@pre(lambda x: isinstance(x, int))`
|
|
56
57
|
- Good: `@pre(lambda x: x > 0 and x < MAX_VALUE)`
|
|
57
58
|
- [ ] Does @post verify meaningful output properties?
|
|
58
59
|
- Bad: `@post(lambda result: result is not None)`
|
|
59
60
|
- Good: `@post(lambda result: len(result) == len(input))`
|
|
61
|
+
{% elif language == "typescript" %}
|
|
62
|
+
- [ ] Does Zod schema constrain inputs beyond type checking?
|
|
63
|
+
- Bad: `z.number()` (type only)
|
|
64
|
+
- Good: `z.number().positive().max(MAX_VALUE)` (semantic constraints)
|
|
65
|
+
- [ ] Does output schema verify meaningful properties?
|
|
66
|
+
- Bad: `z.string()` (type only)
|
|
67
|
+
- Good: `z.string().min(1).max(100)` (semantic constraints)
|
|
68
|
+
{% endif %}
|
|
60
69
|
- [ ] Could someone implement correctly from contracts alone?
|
|
61
70
|
- [ ] Are boundary conditions explicit in contracts?
|
|
62
71
|
|
|
@@ -100,7 +109,11 @@ You ARE here to:
|
|
|
100
109
|
|
|
101
110
|
These are checked by Guard or linters - don't duplicate:
|
|
102
111
|
- Core/Shell separation → Guard (forbidden_import, impure_call)
|
|
112
|
+
{% if language == "python" -%}
|
|
103
113
|
- Shell returns Result[T,E] → Guard (shell_result)
|
|
114
|
+
{% elif language == "typescript" -%}
|
|
115
|
+
- Shell returns Result<T,E> → Guard (shell_result)
|
|
116
|
+
{% endif -%}
|
|
104
117
|
- Missing contracts → Guard (missing_contract)
|
|
105
118
|
- File/function size limits → Guard (file_size, function_size)
|
|
106
119
|
- Entry point thickness → Guard (entry_point_too_thick)
|
|
@@ -121,7 +134,11 @@ For each issue found, use severity levels:
|
|
|
121
134
|
```markdown
|
|
122
135
|
### [CRITICAL/MAJOR/MINOR] Issue Title
|
|
123
136
|
|
|
137
|
+
{% if language == "python" -%}
|
|
124
138
|
**Location:** file.py:line_number
|
|
139
|
+
{% elif language == "typescript" -%}
|
|
140
|
+
**Location:** file.ts:line_number
|
|
141
|
+
{% endif -%}
|
|
125
142
|
**Category:** contract_quality | logic_error | security | escape_hatch | code_smell
|
|
126
143
|
**Problem:** What's wrong
|
|
127
144
|
**Suggestion:** How to fix (but don't implement)
|
|
@@ -8,12 +8,18 @@
|
|
|
8
8
|
{% else -%}
|
|
9
9
|
| **Verify** | `invar guard` — NOT pytest, NOT crosshair |
|
|
10
10
|
{% endif -%}
|
|
11
|
+
{% if language == "python" -%}
|
|
11
12
|
| **Core** | `@pre/@post` + doctests, NO I/O imports |
|
|
12
13
|
| **Shell** | Returns `Result[T, E]` from `returns` library |
|
|
14
|
+
{% elif language == "typescript" -%}
|
|
15
|
+
| **Core** | Zod schemas + JSDoc examples, NO I/O imports |
|
|
16
|
+
| **Shell** | Returns `Result<T, E>` from `neverthrow` library |
|
|
17
|
+
{% endif -%}
|
|
13
18
|
| **Flow** | USBV: Understand → Specify → Build → Validate |
|
|
14
19
|
|
|
15
20
|
### Contract Rules (CRITICAL)
|
|
16
21
|
|
|
22
|
+
{% if language == "python" -%}
|
|
17
23
|
```python
|
|
18
24
|
# ❌ WRONG: Lambda must include ALL parameters
|
|
19
25
|
@pre(lambda x: x >= 0)
|
|
@@ -29,6 +35,21 @@ def calc(x: int, y: int = 0): ...
|
|
|
29
35
|
# ✅ CORRECT: @post only sees 'result'
|
|
30
36
|
@post(lambda result: result >= 0)
|
|
31
37
|
```
|
|
38
|
+
{% elif language == "typescript" -%}
|
|
39
|
+
```typescript
|
|
40
|
+
// ❌ WRONG: Type-only validation (no semantic value)
|
|
41
|
+
const Input = z.number();
|
|
42
|
+
|
|
43
|
+
// ✅ CORRECT: Semantic constraints
|
|
44
|
+
const Input = z.number().nonnegative().max(100);
|
|
45
|
+
|
|
46
|
+
// ❌ WRONG: Output schema cannot reference input
|
|
47
|
+
const Output = z.number().min(x); // 'x' not available!
|
|
48
|
+
|
|
49
|
+
// ✅ CORRECT: Output schema only validates result
|
|
50
|
+
const Output = z.number().nonnegative();
|
|
51
|
+
```
|
|
52
|
+
{% endif -%}
|
|
32
53
|
|
|
33
54
|
<!--/invar:critical-->
|
|
34
55
|
|
|
@@ -67,11 +88,19 @@ This is your sign-out. Completes the Check-In/Final pair.
|
|
|
67
88
|
|
|
68
89
|
## Project Structure
|
|
69
90
|
|
|
91
|
+
{% if language == "python" -%}
|
|
70
92
|
```
|
|
71
93
|
src/{project}/
|
|
72
94
|
├── core/ # Pure logic (@pre/@post, doctests, no I/O)
|
|
73
95
|
└── shell/ # I/O operations (Result[T, E] return type)
|
|
74
96
|
```
|
|
97
|
+
{% elif language == "typescript" -%}
|
|
98
|
+
```
|
|
99
|
+
src/
|
|
100
|
+
├── core/ # Pure logic (Zod schemas, JSDoc examples, no I/O)
|
|
101
|
+
└── shell/ # I/O operations (Result<T, E> return type)
|
|
102
|
+
```
|
|
103
|
+
{% endif -%}
|
|
75
104
|
|
|
76
105
|
**Key insight:** Core receives data (strings), Shell handles I/O (paths, files).
|
|
77
106
|
|
|
@@ -79,8 +108,13 @@ src/{project}/
|
|
|
79
108
|
|
|
80
109
|
| Zone | Requirements |
|
|
81
110
|
|------|-------------|
|
|
111
|
+
{% if language == "python" -%}
|
|
82
112
|
| Core | `@pre`/`@post` + doctests, pure (no I/O) |
|
|
83
113
|
| Shell | Returns `Result[T, E]` from `returns` library |
|
|
114
|
+
{% elif language == "typescript" -%}
|
|
115
|
+
| Core | Zod schemas + JSDoc examples, pure (no I/O) |
|
|
116
|
+
| Shell | Returns `Result<T, E>` from `neverthrow` library |
|
|
117
|
+
{% endif -%}
|
|
84
118
|
|
|
85
119
|
### Core vs Shell (Edge Cases)
|
|
86
120
|
|
|
@@ -118,10 +152,16 @@ For complex tasks (3+ functions), follow these phases:
|
|
|
118
152
|
|
|
119
153
|
### 2. SPECIFY
|
|
120
154
|
|
|
155
|
+
{% if language == "python" -%}
|
|
121
156
|
- **Contracts FIRST:** Write `@pre`/`@post` before implementation
|
|
122
157
|
- **Doctests:** Add examples for expected behavior
|
|
158
|
+
{% elif language == "typescript" -%}
|
|
159
|
+
- **Contracts FIRST:** Write Zod schemas before implementation
|
|
160
|
+
- **JSDoc Examples:** Add `@example` for expected behavior
|
|
161
|
+
{% endif -%}
|
|
123
162
|
- **Design:** Decompose complex tasks into sub-functions
|
|
124
163
|
|
|
164
|
+
{% if language == "python" -%}
|
|
125
165
|
```python
|
|
126
166
|
# SPECIFY before BUILD:
|
|
127
167
|
@pre(lambda x: x > 0)
|
|
@@ -133,6 +173,24 @@ def calculate(x: int) -> int:
|
|
|
133
173
|
"""
|
|
134
174
|
... # Implementation comes in BUILD
|
|
135
175
|
```
|
|
176
|
+
{% elif language == "typescript" -%}
|
|
177
|
+
```typescript
|
|
178
|
+
// SPECIFY before BUILD:
|
|
179
|
+
import { z } from 'zod';
|
|
180
|
+
|
|
181
|
+
const CalculateInput = z.number().positive();
|
|
182
|
+
const CalculateOutput = z.number().nonnegative();
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @example calculate(10) // => 100
|
|
186
|
+
*/
|
|
187
|
+
function calculate(x: number): number {
|
|
188
|
+
const validated = CalculateInput.parse(x);
|
|
189
|
+
// Implementation comes in BUILD
|
|
190
|
+
return CalculateOutput.parse(result);
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
{% endif -%}
|
|
136
194
|
|
|
137
195
|
### 3. BUILD
|
|
138
196
|
|
|
@@ -1,226 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
{%
|
|
7
|
-
|
|
8
|
-
{%
|
|
9
|
-
|
|
10
|
-
{% endif -%}
|
|
11
|
-
| **Core** | `@pre/@post` + doctests, NO I/O imports |
|
|
12
|
-
| **Shell** | Returns `Result[T, E]` from `returns` library |
|
|
13
|
-
| **Flow** | USBV: Understand → Specify → Build → Validate |
|
|
14
|
-
|
|
15
|
-
### Contract Rules (CRITICAL)
|
|
16
|
-
|
|
17
|
-
```python
|
|
18
|
-
# ❌ WRONG: Lambda must include ALL parameters
|
|
19
|
-
@pre(lambda x: x >= 0)
|
|
20
|
-
def calc(x: int, y: int = 0): ...
|
|
21
|
-
|
|
22
|
-
# ✅ CORRECT: Include defaults too
|
|
23
|
-
@pre(lambda x, y=0: x >= 0)
|
|
24
|
-
def calc(x: int, y: int = 0): ...
|
|
25
|
-
|
|
26
|
-
# ❌ WRONG: @post cannot access parameters
|
|
27
|
-
@post(lambda result: result > x) # 'x' not available!
|
|
28
|
-
|
|
29
|
-
# ✅ CORRECT: @post only sees 'result'
|
|
30
|
-
@post(lambda result: result >= 0)
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
<!--/invar:critical-->
|
|
1
|
+
{# CLAUDE.md Composition Template
|
|
2
|
+
Composes universal workflow + language-specific rules
|
|
3
|
+
Variables: language (python|typescript), syntax (mcp|cli), version
|
|
4
|
+
#}
|
|
5
|
+
{% if language == "python" %}
|
|
6
|
+
{% include "claude-md/python/critical-rules.md" %}
|
|
7
|
+
{% elif language == "typescript" %}
|
|
8
|
+
{% include "claude-md/typescript/critical-rules.md" %}
|
|
9
|
+
{% endif %}
|
|
34
10
|
|
|
35
11
|
<!--invar:managed version="{{ version }}"-->
|
|
36
12
|
# Project Development Guide
|
|
37
13
|
|
|
38
14
|
> **Protocol:** Follow [INVAR.md](./INVAR.md) — includes Check-In, USBV workflow, and Task Completion requirements.
|
|
39
15
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
> See [INVAR.md#check-in](./INVAR.md#check-in-required) for full protocol.
|
|
43
|
-
|
|
44
|
-
**Your first message MUST display:** `✓ Check-In: [project] | [branch] | [clean/dirty]`
|
|
45
|
-
|
|
46
|
-
**Actions:** Read `.invar/context.md`, then show status. Do NOT run guard at Check-In.
|
|
16
|
+
{% include "claude-md/universal/check-in.md" %}
|
|
47
17
|
|
|
48
18
|
---
|
|
49
19
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
✓ Final: guard PASS | 0 errors, 2 warnings
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
{% if syntax == "mcp" -%}
|
|
59
|
-
Execute `invar_guard()` and show this one-line summary.
|
|
60
|
-
{% else -%}
|
|
61
|
-
Execute `invar guard` and show this one-line summary.
|
|
20
|
+
{% if language == "python" %}
|
|
21
|
+
{% include "claude-md/python/quick-reference.md" %}
|
|
22
|
+
{% elif language == "typescript" %}
|
|
23
|
+
{% include "claude-md/typescript/quick-reference.md" %}
|
|
62
24
|
{% endif %}
|
|
63
25
|
|
|
64
|
-
|
|
26
|
+
{% include "claude-md/universal/workflow.md" %}
|
|
65
27
|
|
|
66
28
|
---
|
|
67
29
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
```
|
|
71
|
-
src/{project}/
|
|
72
|
-
├── core/ # Pure logic (@pre/@post, doctests, no I/O)
|
|
73
|
-
└── shell/ # I/O operations (Result[T, E] return type)
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
**Key insight:** Core receives data (strings), Shell handles I/O (paths, files).
|
|
77
|
-
|
|
78
|
-
## Quick Reference
|
|
79
|
-
|
|
80
|
-
| Zone | Requirements |
|
|
81
|
-
|------|-------------|
|
|
82
|
-
| Core | `@pre`/`@post` + doctests, pure (no I/O) |
|
|
83
|
-
| Shell | Returns `Result[T, E]` from `returns` library |
|
|
84
|
-
|
|
85
|
-
### Core vs Shell (Edge Cases)
|
|
86
|
-
|
|
87
|
-
- File/network/env vars → **Shell**
|
|
88
|
-
- `datetime.now()`, `random` → **Inject param** OR Shell
|
|
89
|
-
- Pure logic → **Core**
|
|
90
|
-
|
|
91
|
-
> Full decision tree: [INVAR.md#core-shell](./INVAR.md#decision-tree-core-vs-shell)
|
|
92
|
-
|
|
93
|
-
## Documentation Structure
|
|
94
|
-
|
|
95
|
-
| File | Owner | Edit? | Purpose |
|
|
96
|
-
|------|-------|-------|---------|
|
|
97
|
-
| INVAR.md | Invar | No | Protocol (`invar update` to sync) |
|
|
98
|
-
| CLAUDE.md | User | Yes | Project customization (this file) |
|
|
99
|
-
| .invar/context.md | User | Yes | Project state, lessons learned |
|
|
100
|
-
| .invar/project-additions.md | User | Yes | Project rules → injected into CLAUDE.md |
|
|
101
|
-
| .invar/examples/ | Invar | No | **Must read:** Core/Shell patterns, workflow |
|
|
102
|
-
|
|
103
|
-
> **Before writing code:** Check Task Router in `.invar/context.md`
|
|
104
|
-
|
|
105
|
-
## Visible Workflow (DX-30)
|
|
106
|
-
|
|
107
|
-
For complex tasks (3+ functions), show 3 checkpoints in TodoList:
|
|
108
|
-
|
|
109
|
-
```
|
|
110
|
-
□ [UNDERSTAND] Task description, codebase context, constraints
|
|
111
|
-
□ [SPECIFY] Contracts (@pre/@post) and design decomposition
|
|
112
|
-
□ [VALIDATE] Guard results, Review Gate status, integration status
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
**BUILD is internal work** — not shown in TodoList.
|
|
116
|
-
|
|
117
|
-
**Show contracts before code.** See `.invar/examples/workflow.md` for full example.
|
|
118
|
-
|
|
119
|
-
## Phase Visibility (DX-51)
|
|
120
|
-
|
|
121
|
-
Each USBV phase transition requires a visible header:
|
|
122
|
-
|
|
123
|
-
```
|
|
124
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
125
|
-
📍 /develop → SPECIFY (2/4)
|
|
126
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
**Three-layer visibility:**
|
|
130
|
-
- **Skill** (`/develop`) — Routing announcement
|
|
131
|
-
- **Phase** (`SPECIFY 2/4`) — Phase header (this section)
|
|
132
|
-
- **Tasks** — TodoWrite items
|
|
133
|
-
|
|
134
|
-
Phase headers are SEPARATE from TodoWrite. Phase = where you are; TodoWrite = what to do.
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
## Context Management (DX-54)
|
|
139
|
-
|
|
140
|
-
Re-read `.invar/context.md` when:
|
|
141
|
-
1. Entering any workflow (/develop, /review, etc.)
|
|
142
|
-
2. Completing a TodoWrite task (before moving to next)
|
|
143
|
-
3. Conversation exceeds ~15-20 exchanges
|
|
144
|
-
4. Unsure about project rules or patterns
|
|
145
|
-
|
|
146
|
-
**Refresh is transparent** — do not announce "I'm refreshing context."
|
|
147
|
-
Only show routing announcements when entering workflows.
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
## Commands (User-Invokable)
|
|
152
|
-
|
|
153
|
-
| Command | Purpose |
|
|
154
|
-
|---------|---------|
|
|
155
|
-
| `/audit` | Read-only code review (reports issues, no fixes) |
|
|
156
|
-
| `/guard` | Run Invar verification (reports results) |
|
|
157
|
-
|
|
158
|
-
## Skills (Agent-Invoked)
|
|
159
|
-
|
|
160
|
-
| Skill | Triggers | Purpose |
|
|
161
|
-
|-------|----------|---------|
|
|
162
|
-
| `/investigate` | "why", "explain", vague tasks | Research mode, no code changes |
|
|
163
|
-
| `/propose` | "should we", "compare" | Decision facilitation |
|
|
164
|
-
| `/develop` | "add", "fix", "implement" | USBV implementation workflow |
|
|
165
|
-
| `/review` | After /develop, `review_suggested` | Adversarial review with fix loop |
|
|
166
|
-
|
|
167
|
-
**Note:** Skills are invoked by agent based on context. Use `/audit` for user-initiated review.
|
|
168
|
-
|
|
169
|
-
Guard triggers `review_suggested` for: security-sensitive files, escape hatches >= 3, contract coverage < 50%.
|
|
170
|
-
|
|
171
|
-
---
|
|
172
|
-
|
|
173
|
-
## Workflow Routing (MANDATORY)
|
|
174
|
-
|
|
175
|
-
When user message contains these triggers, you MUST use the **Skill tool** to invoke the skill:
|
|
176
|
-
|
|
177
|
-
| Trigger Words | Skill Tool Call | Notes |
|
|
178
|
-
|---------------|-----------------|-------|
|
|
179
|
-
| "review", "review and fix" | `Skill(skill="review")` | Adversarial review with fix loop |
|
|
180
|
-
| "implement", "add", "fix", "update" | `Skill(skill="develop")` | Unless in review context |
|
|
181
|
-
| "why", "explain", "investigate" | `Skill(skill="investigate")` | Research mode, no code changes |
|
|
182
|
-
| "compare", "should we", "design" | `Skill(skill="propose")` | Decision facilitation |
|
|
183
|
-
|
|
184
|
-
**⚠️ CRITICAL: You must call the Skill tool, not just follow the workflow mentally.**
|
|
185
|
-
|
|
186
|
-
The Skill tool reads `.claude/skills/<skill>/SKILL.md` which contains:
|
|
187
|
-
- Detailed phase instructions (USBV breakdown)
|
|
188
|
-
- Error handling rules
|
|
189
|
-
- Timeout policies
|
|
190
|
-
- Incremental development patterns (DX-63)
|
|
191
|
-
|
|
192
|
-
**Violation check (before writing ANY code):**
|
|
193
|
-
- "Did I call `Skill(skill="...")`?"
|
|
194
|
-
- "Am I following the SKILL.md instructions?"
|
|
195
|
-
|
|
196
|
-
---
|
|
197
|
-
|
|
198
|
-
## Routing Control (DX-42)
|
|
199
|
-
|
|
200
|
-
Agent announces routing decision before entering any workflow:
|
|
201
|
-
|
|
202
|
-
```
|
|
203
|
-
📍 Routing: /[skill] — [trigger or reason]
|
|
204
|
-
Task: [summary]
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
**User can redirect with natural language:**
|
|
208
|
-
- "wait" / "stop" — pause and ask for direction
|
|
209
|
-
- "just do it" — proceed with /develop
|
|
210
|
-
- "let's discuss" — switch to /propose
|
|
211
|
-
- "explain first" — switch to /investigate
|
|
212
|
-
|
|
213
|
-
**Simple task optimization:** For simple tasks (single file, clear target, <50 lines), agent may offer:
|
|
214
|
-
|
|
215
|
-
```
|
|
216
|
-
📊 Simple task. Auto-orchestrate? [Y/N]
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
- Y → Full cycle without intermediate confirmations
|
|
220
|
-
- N → Normal step-by-step workflow
|
|
221
|
-
|
|
222
|
-
**Auto-review (DX-41):** When Guard outputs `review_suggested`, agent automatically
|
|
223
|
-
enters /review. Say "skip" to bypass.
|
|
30
|
+
{% include "claude-md/universal/skills.md" %}
|
|
224
31
|
<!--/invar:managed-->
|
|
225
32
|
|
|
226
33
|
<!--invar:project-->
|
|
@@ -13,8 +13,13 @@
|
|
|
13
13
|
<!-- DX-54: Rules summary for long conversation resilience -->
|
|
14
14
|
|
|
15
15
|
### Core/Shell Separation
|
|
16
|
+
{% if language == "python" -%}
|
|
16
17
|
- **Core** (`**/core/**`): @pre/@post + doctests, NO I/O imports
|
|
17
18
|
- **Shell** (`**/shell/**`): Result[T, E] return type
|
|
19
|
+
{% elif language == "typescript" -%}
|
|
20
|
+
- **Core** (`**/core/**`): Contract comments (@pre/@post), no side effects
|
|
21
|
+
- **Shell** (`**/shell/**`): Result<T, E> return type
|
|
22
|
+
{% endif -%}
|
|
18
23
|
|
|
19
24
|
### USBV Workflow
|
|
20
25
|
1. Understand → 2. Specify (contracts first) → 3. Build → 4. Validate
|
|
@@ -31,6 +36,7 @@
|
|
|
31
36
|
|
|
32
37
|
<!-- Before writing code, check this table -->
|
|
33
38
|
|
|
39
|
+
{% if language == "python" %}
|
|
34
40
|
| If you are about to... | STOP and read first |
|
|
35
41
|
|------------------------|---------------------|
|
|
36
42
|
| Write code in `core/` | `.invar/examples/contracts.py` |
|
|
@@ -38,6 +44,15 @@
|
|
|
38
44
|
| Add `@pre`/`@post` contracts | `.invar/examples/contracts.py` |
|
|
39
45
|
| Use functional patterns | `.invar/examples/functional.py` |
|
|
40
46
|
| Implement a feature | `.invar/examples/workflow.md` |
|
|
47
|
+
{% elif language == "typescript" %}
|
|
48
|
+
| If you are about to... | STOP and read first |
|
|
49
|
+
|------------------------|---------------------|
|
|
50
|
+
| Write code in `core/` | `.invar/examples/contracts.ts` |
|
|
51
|
+
| Write code in `shell/` | `.invar/examples/core_shell.ts` |
|
|
52
|
+
| Add contract comments | `.invar/examples/contracts.ts` |
|
|
53
|
+
| Use functional patterns | `.invar/examples/functional.ts` |
|
|
54
|
+
| Implement a feature | `.invar/examples/workflow.md` |
|
|
55
|
+
{% endif %}
|
|
41
56
|
|
|
42
57
|
**Rule:** Match found above? Read the file BEFORE writing code.
|
|
43
58
|
|
|
@@ -53,7 +68,11 @@
|
|
|
53
68
|
|
|
54
69
|
**Quick rule check:**
|
|
55
70
|
- Am I in Core or Shell?
|
|
71
|
+
{% if language == "python" -%}
|
|
56
72
|
- Do I have @pre/@post contracts?
|
|
73
|
+
{% elif language == "typescript" -%}
|
|
74
|
+
- Do I have contract comments (@pre/@post)?
|
|
75
|
+
{% endif -%}
|
|
57
76
|
- Am I following USBV workflow?
|
|
58
77
|
- Did I run guard before claiming "done"?
|
|
59
78
|
|
|
@@ -8,6 +8,7 @@ Reference examples for the Invar Protocol. These are managed by Invar.
|
|
|
8
8
|
|------|---------|
|
|
9
9
|
| [contracts.py](contracts.py) | @pre/@post patterns, doctest best practices |
|
|
10
10
|
| [core_shell.py](core_shell.py) | Core/Shell separation patterns |
|
|
11
|
+
| [functional.py](functional.py) | Functional patterns (NewType, Validation, NonEmpty, Literal) |
|
|
11
12
|
| [workflow.md](workflow.md) | Visible USBV workflow example |
|
|
12
13
|
|
|
13
14
|
## Usage
|
|
@@ -16,6 +17,7 @@ Read these when you need:
|
|
|
16
17
|
- Contract pattern reference
|
|
17
18
|
- Core vs Shell decision guidance
|
|
18
19
|
- Doctest formatting examples
|
|
20
|
+
- Functional pattern suggestions
|
|
19
21
|
- USBV workflow example
|
|
20
22
|
|
|
21
23
|
---
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
# ruff: noqa: ERA001
|
|
1
2
|
"""
|
|
2
3
|
Invar Contract Examples
|
|
3
4
|
|
|
4
5
|
Reference patterns for @pre/@post contracts and doctests.
|
|
5
6
|
Managed by Invar - do not edit directly.
|
|
6
7
|
"""
|
|
8
|
+
# @invar:allow partial_contract: Educational file showing multiple @pre pattern
|
|
7
9
|
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
from deal import post, pre
|
|
10
|
+
# invar_runtime supports both lambda and Contract objects
|
|
11
|
+
from invar_runtime import post, pre
|
|
11
12
|
|
|
12
13
|
# =============================================================================
|
|
13
14
|
# GOOD: Complete Contract
|
|
@@ -80,7 +81,7 @@ def normalize_keys(data: dict[str, int]) -> dict[str, int]:
|
|
|
80
81
|
# DON'T: Empty contract tells nothing
|
|
81
82
|
# @pre(lambda: True)
|
|
82
83
|
# @post(lambda result: True)
|
|
83
|
-
# def process(x): ...
|
|
84
|
+
# def process(x): ...
|
|
84
85
|
|
|
85
86
|
# DON'T: Missing edge cases in doctests
|
|
86
87
|
# def divide(a, b):
|
|
@@ -111,3 +112,79 @@ def range_size(start: int, end: int) -> int:
|
|
|
111
112
|
1
|
|
112
113
|
"""
|
|
113
114
|
return end - start
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# =============================================================================
|
|
118
|
+
# CRITICAL: Default Parameters in Contracts
|
|
119
|
+
# =============================================================================
|
|
120
|
+
# Lambda signatures MUST include ALL parameters, including defaults!
|
|
121
|
+
# This is a common mistake that causes silent contract failures.
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# DON'T: Missing default parameter in lambda
|
|
125
|
+
# @pre(lambda x: x >= 0) # BAD: 'y' is missing!
|
|
126
|
+
# def calc_bad(x: int, y: int = 0) -> int:
|
|
127
|
+
# return x + y
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# DO: Include all parameters with their defaults
|
|
131
|
+
@pre(lambda x, y=0: x >= 0 and y >= 0)
|
|
132
|
+
@post(lambda result: result >= 0)
|
|
133
|
+
def calculate_with_default(x: int, y: int = 0) -> int:
|
|
134
|
+
"""
|
|
135
|
+
Calculate sum with optional y.
|
|
136
|
+
|
|
137
|
+
The lambda MUST include y=0 to match the function signature.
|
|
138
|
+
|
|
139
|
+
>>> calculate_with_default(5)
|
|
140
|
+
5
|
|
141
|
+
>>> calculate_with_default(5, 3)
|
|
142
|
+
8
|
|
143
|
+
>>> calculate_with_default(0, 0) # Edge: both zero
|
|
144
|
+
0
|
|
145
|
+
"""
|
|
146
|
+
return x + y
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# =============================================================================
|
|
150
|
+
# CRITICAL: @post Cannot Access Parameters
|
|
151
|
+
# =============================================================================
|
|
152
|
+
# @post only receives the return value, not the original parameters!
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# DON'T: Reference parameters in @post
|
|
156
|
+
# @post(lambda result: result > x) # BAD: 'x' is not available!
|
|
157
|
+
# def double_bad(x: int) -> int:
|
|
158
|
+
# return x * 2
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# DO: @post only validates the result itself
|
|
162
|
+
@pre(lambda x: x >= 0)
|
|
163
|
+
@post(lambda result: result >= 0) # GOOD: only uses 'result'
|
|
164
|
+
def double_positive(x: int) -> int:
|
|
165
|
+
"""
|
|
166
|
+
Double a positive number.
|
|
167
|
+
|
|
168
|
+
@post can only validate result properties, not relationships to inputs.
|
|
169
|
+
|
|
170
|
+
>>> double_positive(5)
|
|
171
|
+
10
|
|
172
|
+
>>> double_positive(0) # Edge: zero
|
|
173
|
+
0
|
|
174
|
+
"""
|
|
175
|
+
return x * 2
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# =============================================================================
|
|
179
|
+
# Decorator Order with @pre/@post
|
|
180
|
+
# =============================================================================
|
|
181
|
+
# When combining with other decorators, @pre/@post should be closest to function.
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# DO: @pre/@post closest to function
|
|
185
|
+
# @other_decorator
|
|
186
|
+
# @pre(lambda x: x > 0)
|
|
187
|
+
# @post(lambda result: result > 0)
|
|
188
|
+
# def my_func(x: int) -> int: ...
|
|
189
|
+
|
|
190
|
+
# This ensures contracts run BEFORE other decorators modify behavior.
|