invar-tools 1.2.0__py3-none-any.whl → 1.3.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 +1 -0
- invar/core/contracts.py +10 -10
- invar/core/entry_points.py +105 -32
- invar/core/extraction.py +5 -6
- invar/core/format_specs.py +1 -2
- invar/core/formatter.py +6 -7
- invar/core/hypothesis_strategies.py +5 -7
- invar/core/inspect.py +1 -1
- invar/core/lambda_helpers.py +3 -3
- invar/core/models.py +7 -1
- invar/core/must_use.py +2 -1
- invar/core/parser.py +7 -4
- invar/core/postcondition_scope.py +128 -0
- invar/core/property_gen.py +8 -5
- invar/core/purity.py +3 -3
- invar/core/purity_heuristics.py +5 -9
- invar/core/references.py +8 -6
- invar/core/review_trigger.py +78 -6
- invar/core/rule_meta.py +8 -0
- invar/core/rules.py +18 -19
- invar/core/shell_analysis.py +5 -10
- invar/core/shell_architecture.py +2 -2
- invar/core/strategies.py +7 -14
- invar/core/suggestions.py +86 -0
- invar/core/sync_helpers.py +238 -0
- invar/core/tautology.py +102 -37
- invar/core/template_parser.py +467 -0
- invar/core/timeout_inference.py +4 -7
- invar/core/utils.py +13 -15
- invar/core/verification_routing.py +4 -7
- invar/mcp/server.py +100 -17
- invar/shell/commands/__init__.py +11 -0
- invar/shell/{cli.py → commands/guard.py} +94 -14
- invar/shell/{init_cmd.py → commands/init.py} +179 -27
- invar/shell/commands/merge.py +256 -0
- invar/shell/commands/sync_self.py +113 -0
- invar/shell/commands/template_sync.py +366 -0
- invar/shell/commands/update.py +48 -0
- invar/shell/config.py +12 -24
- invar/shell/coverage.py +351 -0
- invar/shell/guard_helpers.py +38 -17
- invar/shell/guard_output.py +7 -1
- invar/shell/property_tests.py +58 -22
- invar/shell/prove/__init__.py +9 -0
- invar/shell/{prove.py → prove/crosshair.py} +40 -33
- invar/shell/{prove_fallback.py → prove/hypothesis.py} +12 -4
- invar/shell/subprocess_env.py +393 -0
- invar/shell/template_engine.py +345 -0
- invar/shell/templates.py +19 -0
- invar/shell/testing.py +71 -20
- invar/templates/CLAUDE.md.template +38 -17
- invar/templates/aider.conf.yml.template +2 -2
- invar/templates/commands/{review.md → audit.md} +20 -82
- invar/templates/commands/guard.md +77 -0
- invar/templates/config/CLAUDE.md.jinja +206 -0
- invar/templates/config/context.md.jinja +92 -0
- invar/templates/config/pre-commit.yaml.jinja +44 -0
- invar/templates/context.md.template +33 -0
- invar/templates/cursorrules.template +7 -4
- invar/templates/examples/README.md +2 -0
- invar/templates/examples/conftest.py +3 -0
- invar/templates/examples/contracts.py +5 -5
- invar/templates/examples/core_shell.py +11 -7
- invar/templates/examples/workflow.md +81 -0
- invar/templates/manifest.toml +137 -0
- invar/templates/{INVAR.md → protocol/INVAR.md} +10 -7
- invar/templates/skills/develop/SKILL.md.jinja +318 -0
- invar/templates/skills/investigate/SKILL.md.jinja +106 -0
- invar/templates/skills/propose/SKILL.md.jinja +104 -0
- invar/templates/skills/review/SKILL.md.jinja +125 -0
- {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/METADATA +108 -118
- invar_tools-1.3.0.dist-info/RECORD +95 -0
- invar_tools-1.3.0.dist-info/entry_points.txt +2 -0
- invar/contracts.py +0 -152
- invar/decorators.py +0 -94
- invar/invariant.py +0 -58
- invar/resource.py +0 -99
- invar/shell/update_cmd.py +0 -193
- invar_tools-1.2.0.dist-info/RECORD +0 -77
- invar_tools-1.2.0.dist-info/entry_points.txt +0 -2
- /invar/shell/{mutate_cmd.py → commands/mutate.py} +0 -0
- /invar/shell/{perception.py → commands/perception.py} +0 -0
- /invar/shell/{test_cmd.py → commands/test.py} +0 -0
- /invar/shell/{prove_accept.py → prove/accept.py} +0 -0
- /invar/shell/{prove_cache.py → prove/cache.py} +0 -0
- {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/WHEEL +0 -0
- {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/licenses/LICENSE-GPL +0 -0
- {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -7,9 +7,9 @@ Managed by Invar - do not edit directly.
|
|
|
7
7
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
from
|
|
10
|
+
# For lambda-based contracts, use deal directly
|
|
11
|
+
# invar_runtime.pre/post are for Contract objects (NonEmpty, IsInstance, etc.)
|
|
12
|
+
from deal import post, pre
|
|
13
13
|
from returns.result import Failure, Result, Success
|
|
14
14
|
|
|
15
15
|
# =============================================================================
|
|
@@ -20,8 +20,10 @@ from returns.result import Failure, Result, Success
|
|
|
20
20
|
# =============================================================================
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
@
|
|
24
|
-
@
|
|
23
|
+
# @invar:allow shell_result: Example file - demonstrates Core pattern
|
|
24
|
+
# @shell_orchestration: Example file - demonstrates Core pattern
|
|
25
|
+
@pre(lambda content: content is not None) # Accepts any string including empty
|
|
26
|
+
@post(lambda result: all(line.strip() == line and line for line in result)) # No whitespace, non-empty
|
|
25
27
|
def parse_lines(content: str) -> list[str]:
|
|
26
28
|
"""
|
|
27
29
|
Parse content into non-empty lines.
|
|
@@ -36,8 +38,10 @@ def parse_lines(content: str) -> list[str]:
|
|
|
36
38
|
return [line.strip() for line in content.split("\n") if line.strip()]
|
|
37
39
|
|
|
38
40
|
|
|
39
|
-
@
|
|
40
|
-
@
|
|
41
|
+
# @invar:allow shell_result: Example file - demonstrates Core pattern
|
|
42
|
+
# @shell_orchestration: Example file - demonstrates Core pattern
|
|
43
|
+
@pre(lambda items: all(isinstance(i, str) for i in items)) # All items must be strings
|
|
44
|
+
@post(lambda result: all(v > 0 for v in result.values())) # All counts are positive
|
|
41
45
|
def count_items(items: list[str]) -> dict[str, int]:
|
|
42
46
|
"""
|
|
43
47
|
Count occurrences of each item.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Visible Workflow Example
|
|
2
|
+
|
|
3
|
+
Complete example of USBV workflow with checkpoints for complex tasks.
|
|
4
|
+
|
|
5
|
+
## Scenario
|
|
6
|
+
|
|
7
|
+
User: "Add rate limiting to the API endpoints"
|
|
8
|
+
|
|
9
|
+
## Workflow Execution
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Agent: "Entering /develop for: Add rate limiting to API endpoints
|
|
13
|
+
|
|
14
|
+
✓ Check-In: MyApp | feature/rate-limit | clean
|
|
15
|
+
|
|
16
|
+
□ [UNDERSTAND]
|
|
17
|
+
- Task: Add rate limiting to API endpoints
|
|
18
|
+
- Scope: All routes in src/myapp/shell/api.py
|
|
19
|
+
- Constraint: Must be configurable per-route
|
|
20
|
+
- Existing: No rate limiting currently
|
|
21
|
+
|
|
22
|
+
□ [SPECIFY] RateLimiter class (Core)
|
|
23
|
+
@pre(lambda max_requests, window_seconds: max_requests > 0 and window_seconds > 0)
|
|
24
|
+
@post(lambda result: result is not None)
|
|
25
|
+
def is_allowed(self, key: str) -> bool:
|
|
26
|
+
'''
|
|
27
|
+
>>> limiter = RateLimiter(max_requests=2, window_seconds=60)
|
|
28
|
+
>>> limiter.is_allowed('user1')
|
|
29
|
+
True
|
|
30
|
+
>>> limiter.is_allowed('user1')
|
|
31
|
+
True
|
|
32
|
+
>>> limiter.is_allowed('user1') # 3rd request
|
|
33
|
+
False
|
|
34
|
+
'''
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
□ [SPECIFY] rate_limit decorator (Shell)
|
|
38
|
+
def rate_limit(max_requests: int, window_seconds: int):
|
|
39
|
+
'''Decorator for Flask routes. Returns Result[Response, RateLimitError].'''
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
[BUILD - implementing RateLimiter...]
|
|
43
|
+
[BUILD - implementing decorator...]
|
|
44
|
+
[BUILD - applying to routes...]
|
|
45
|
+
|
|
46
|
+
□ [VALIDATE]
|
|
47
|
+
- Guard: PASS (0 errors, 0 warnings)
|
|
48
|
+
- Tests: All passing
|
|
49
|
+
- Integration: Manual test with curl
|
|
50
|
+
|
|
51
|
+
✓ Final: guard PASS | 0 errors, 0 warnings
|
|
52
|
+
|
|
53
|
+
Development complete. Added rate limiting:
|
|
54
|
+
- Created: src/myapp/core/rate_limiter.py
|
|
55
|
+
- Modified: src/myapp/shell/api.py (decorator applied)
|
|
56
|
+
- Config: RATE_LIMIT_REQUESTS, RATE_LIMIT_WINDOW in .env"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Key Points
|
|
60
|
+
|
|
61
|
+
1. **Check-In first** - Shows project, branch, git status (no guard/map here)
|
|
62
|
+
2. **UNDERSTAND visible** - Task scope and constraints clear
|
|
63
|
+
3. **SPECIFY visible** - Contracts shown BEFORE implementation
|
|
64
|
+
4. **BUILD hidden** - Internal work, not in checkpoints
|
|
65
|
+
5. **VALIDATE visible** - Guard results and integration status
|
|
66
|
+
6. **Final last** - Runs guard and completes the session
|
|
67
|
+
|
|
68
|
+
## When to Use
|
|
69
|
+
|
|
70
|
+
| Complexity | Use Visible Workflow? |
|
|
71
|
+
|------------|----------------------|
|
|
72
|
+
| 3+ functions | Yes |
|
|
73
|
+
| Architectural changes | Yes |
|
|
74
|
+
| New Core module | Yes |
|
|
75
|
+
| Single-line fix | No |
|
|
76
|
+
| Documentation only | No |
|
|
77
|
+
| Trivial refactoring | No |
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
*Example for the Invar Protocol v5.0*
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# DX-49: Template Manifest
|
|
2
|
+
# Defines ownership rules and template behaviors
|
|
3
|
+
|
|
4
|
+
[meta]
|
|
5
|
+
version = "5.0"
|
|
6
|
+
workflow = "USBV"
|
|
7
|
+
|
|
8
|
+
# =============================================================================
|
|
9
|
+
# Ownership Classification
|
|
10
|
+
# =============================================================================
|
|
11
|
+
|
|
12
|
+
[ownership]
|
|
13
|
+
# Fully managed - safe to overwrite completely
|
|
14
|
+
fully_managed = [
|
|
15
|
+
"INVAR.md",
|
|
16
|
+
".invar/examples/**",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
# Partially managed - only update marked regions
|
|
20
|
+
partially_managed = [
|
|
21
|
+
"CLAUDE.md",
|
|
22
|
+
".claude/skills/*/SKILL.md",
|
|
23
|
+
".claude/commands/*.md",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
# Never touch - other tools or user private files
|
|
27
|
+
never_touch = [
|
|
28
|
+
".claude/settings*.json",
|
|
29
|
+
".mcp.json",
|
|
30
|
+
".cursorrules",
|
|
31
|
+
".aider*",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
# =============================================================================
|
|
35
|
+
# Region Definitions
|
|
36
|
+
# =============================================================================
|
|
37
|
+
|
|
38
|
+
[regions."CLAUDE.md"]
|
|
39
|
+
managed = { action = "overwrite" }
|
|
40
|
+
project = { action = "inject", source = ".invar/project-additions.md" }
|
|
41
|
+
user = { action = "preserve" }
|
|
42
|
+
|
|
43
|
+
[regions.".claude/skills/*/SKILL.md"]
|
|
44
|
+
skill = { action = "overwrite" }
|
|
45
|
+
extensions = { action = "preserve" }
|
|
46
|
+
|
|
47
|
+
# =============================================================================
|
|
48
|
+
# Template Generation
|
|
49
|
+
# =============================================================================
|
|
50
|
+
|
|
51
|
+
[templates]
|
|
52
|
+
# Protocol files (direct copy)
|
|
53
|
+
"INVAR.md" = { src = "protocol/INVAR.md", type = "copy" }
|
|
54
|
+
|
|
55
|
+
# Config files (Jinja2 templates)
|
|
56
|
+
"CLAUDE.md" = { src = "config/CLAUDE.md.jinja", type = "jinja" }
|
|
57
|
+
".invar/context.md" = { src = "config/context.md.jinja", type = "jinja" }
|
|
58
|
+
".pre-commit-config.yaml" = { src = "config/pre-commit.yaml.jinja", type = "jinja" }
|
|
59
|
+
|
|
60
|
+
# Skills (Jinja2 with syntax variable)
|
|
61
|
+
".claude/skills/develop/SKILL.md" = { src = "skills/develop/SKILL.md.jinja", type = "jinja" }
|
|
62
|
+
".claude/skills/investigate/SKILL.md" = { src = "skills/investigate/SKILL.md.jinja", type = "jinja" }
|
|
63
|
+
".claude/skills/propose/SKILL.md" = { src = "skills/propose/SKILL.md.jinja", type = "jinja" }
|
|
64
|
+
".claude/skills/review/SKILL.md" = { src = "skills/review/SKILL.md.jinja", type = "jinja" }
|
|
65
|
+
|
|
66
|
+
# Commands (direct copy)
|
|
67
|
+
".claude/commands/audit.md" = { src = "commands/audit.md", type = "copy" }
|
|
68
|
+
".claude/commands/guard.md" = { src = "commands/guard.md", type = "copy" }
|
|
69
|
+
|
|
70
|
+
# Examples (directory copy)
|
|
71
|
+
".invar/examples/" = { src = "examples/", type = "copy_dir" }
|
|
72
|
+
|
|
73
|
+
# =============================================================================
|
|
74
|
+
# Variables
|
|
75
|
+
# =============================================================================
|
|
76
|
+
|
|
77
|
+
[variables]
|
|
78
|
+
# Available for Jinja2 templates
|
|
79
|
+
syntax = "cli" # "cli" or "mcp"
|
|
80
|
+
version = "5.0"
|
|
81
|
+
project_name = "" # Set by init
|
|
82
|
+
|
|
83
|
+
# =============================================================================
|
|
84
|
+
# Command Behaviors
|
|
85
|
+
# =============================================================================
|
|
86
|
+
|
|
87
|
+
[commands.init]
|
|
88
|
+
# Files created by invar init
|
|
89
|
+
creates = [
|
|
90
|
+
"INVAR.md",
|
|
91
|
+
"CLAUDE.md",
|
|
92
|
+
".invar/context.md",
|
|
93
|
+
".invar/examples/",
|
|
94
|
+
".claude/skills/",
|
|
95
|
+
".claude/commands/",
|
|
96
|
+
".pre-commit-config.yaml",
|
|
97
|
+
]
|
|
98
|
+
syntax = "cli" # Default syntax for new projects
|
|
99
|
+
|
|
100
|
+
[commands.update]
|
|
101
|
+
# How invar update handles existing files
|
|
102
|
+
overwrite = ["INVAR.md", ".invar/examples/"]
|
|
103
|
+
merge = ["CLAUDE.md", ".claude/skills/"]
|
|
104
|
+
skip = [".invar/context.md"]
|
|
105
|
+
|
|
106
|
+
[commands.sync_self]
|
|
107
|
+
# For Invar project only
|
|
108
|
+
syntax = "mcp"
|
|
109
|
+
inject_project_additions = true
|
|
110
|
+
|
|
111
|
+
# =============================================================================
|
|
112
|
+
# DX-56: Sync Configuration (Unified Sync Engine)
|
|
113
|
+
# =============================================================================
|
|
114
|
+
|
|
115
|
+
[sync]
|
|
116
|
+
# Files that are fully managed (overwrite completely on sync)
|
|
117
|
+
fully_managed = [
|
|
118
|
+
"INVAR.md",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
# Files with region-based updates (update managed, preserve user)
|
|
122
|
+
region_managed = [
|
|
123
|
+
"CLAUDE.md",
|
|
124
|
+
".claude/skills/develop/SKILL.md",
|
|
125
|
+
".claude/skills/investigate/SKILL.md",
|
|
126
|
+
".claude/skills/propose/SKILL.md",
|
|
127
|
+
".claude/skills/review/SKILL.md",
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
# Files created once, never updated by sync
|
|
131
|
+
create_only = [
|
|
132
|
+
".invar/context.md",
|
|
133
|
+
".invar/examples/",
|
|
134
|
+
".pre-commit-config.yaml",
|
|
135
|
+
".claude/commands/audit.md",
|
|
136
|
+
".claude/commands/guard.md",
|
|
137
|
+
]
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
You are free to share and adapt this document, provided you give
|
|
13
13
|
appropriate credit to the Invar project.
|
|
14
14
|
-->
|
|
15
|
-
# The Invar Protocol
|
|
15
|
+
# The Invar Protocol v5.0
|
|
16
16
|
|
|
17
17
|
> **"Trade structure for safety."**
|
|
18
18
|
|
|
@@ -81,16 +81,19 @@ More examples: `.invar/examples/`
|
|
|
81
81
|
Your first message MUST display:
|
|
82
82
|
|
|
83
83
|
```
|
|
84
|
-
✓ Check-In:
|
|
84
|
+
✓ Check-In: [project] | [branch] | [clean/dirty]
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
Actions:
|
|
88
|
+
1. Read `.invar/context.md` (Key Rules + Current State + Lessons Learned)
|
|
89
|
+
2. Show one-line status
|
|
90
|
+
|
|
91
|
+
**Do NOT execute guard or map at Check-In.**
|
|
92
|
+
Guard is for VALIDATE phase and Final only.
|
|
88
93
|
|
|
89
94
|
This is your sign-in. The user sees it immediately.
|
|
90
95
|
No visible check-in = Session not started.
|
|
91
96
|
|
|
92
|
-
Then read `.invar/context.md` for project state and lessons learned.
|
|
93
|
-
|
|
94
97
|
## USBV Workflow (DX-32)
|
|
95
98
|
|
|
96
99
|
**U**nderstand → **S**pecify → **B**uild → **V**alidate
|
|
@@ -135,7 +138,7 @@ def calculate_discount(price: float, rate: float) -> float: ...
|
|
|
135
138
|
## Task Completion
|
|
136
139
|
|
|
137
140
|
A task is complete only when ALL conditions are met:
|
|
138
|
-
- Check-In displayed: `✓ Check-In:
|
|
141
|
+
- Check-In displayed: `✓ Check-In: [project] | [branch] | [clean/dirty]`
|
|
139
142
|
- Intent explicitly stated
|
|
140
143
|
- Contract written before implementation
|
|
141
144
|
- Final displayed: `✓ Final: guard PASS | <errors>, <warnings>`
|
|
@@ -204,4 +207,4 @@ shell_paths = ["src/myapp/shell"]
|
|
|
204
207
|
|
|
205
208
|
---
|
|
206
209
|
|
|
207
|
-
*Protocol
|
|
210
|
+
*Protocol v5.0 — USBV workflow (DX-32) | [Guide](docs/guide.md) | [Examples](.invar/examples/)*
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
<!--invar:skill version="{{ version }}"-->
|
|
2
|
+
<!-- ========================================================================
|
|
3
|
+
SKILL REGION - DO NOT EDIT
|
|
4
|
+
This section is managed by Invar and will be overwritten on update.
|
|
5
|
+
To add project-specific extensions, use the "extensions" region below.
|
|
6
|
+
======================================================================== -->
|
|
7
|
+
---
|
|
8
|
+
name: develop
|
|
9
|
+
description: Implementation phase following USBV workflow. Use when task is clear and actionable - "add", "implement", "create", "fix", "update", "build", "write". Requires Check-In at start and Final at end.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Development Mode
|
|
13
|
+
|
|
14
|
+
> **Purpose:** Implement solution following USBV workflow with verification.
|
|
15
|
+
|
|
16
|
+
## Entry Actions (REQUIRED)
|
|
17
|
+
|
|
18
|
+
### Context Refresh (DX-54)
|
|
19
|
+
|
|
20
|
+
Before any workflow action:
|
|
21
|
+
1. Read `.invar/context.md` (especially Key Rules section)
|
|
22
|
+
2. Display routing announcement
|
|
23
|
+
|
|
24
|
+
### Routing Announcement
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
📍 Routing: /develop — [trigger detected, e.g. "add", "fix", "implement"]
|
|
28
|
+
Task: [user's request summary]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Simple Task Detection
|
|
32
|
+
|
|
33
|
+
If task appears simple (4+ signals: single file, clear target, additive change, <50 lines):
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
📊 Simple task (1 file, ~N lines).
|
|
37
|
+
Auto-orchestrate: investigate → develop → validate?
|
|
38
|
+
[Y/N]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- Y → Execute full cycle without intermediate confirmations
|
|
42
|
+
- N → Proceed with normal USBV checkpoints
|
|
43
|
+
- No response → Default to step-by-step (safe)
|
|
44
|
+
|
|
45
|
+
## USBV Workflow
|
|
46
|
+
|
|
47
|
+
### 1. UNDERSTAND
|
|
48
|
+
|
|
49
|
+
- **Intent:** What exactly needs to be done?
|
|
50
|
+
{% if syntax == "mcp" -%}
|
|
51
|
+
- **Inspect:** Use `invar_sig` to see existing contracts
|
|
52
|
+
{% else -%}
|
|
53
|
+
- **Inspect:** Use `invar sig` to see existing contracts
|
|
54
|
+
{% endif -%}
|
|
55
|
+
- **Context:** Read relevant code, understand patterns
|
|
56
|
+
- **Constraints:** What must NOT change?
|
|
57
|
+
|
|
58
|
+
### 2. SPECIFY
|
|
59
|
+
|
|
60
|
+
- **Contracts FIRST:** Write `@pre`/`@post` before implementation
|
|
61
|
+
- **Doctests:** Add examples for expected behavior
|
|
62
|
+
- **Design:** Decompose complex tasks into sub-functions
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
# SPECIFY before BUILD:
|
|
66
|
+
@pre(lambda x: x > 0)
|
|
67
|
+
@post(lambda result: result >= 0)
|
|
68
|
+
def calculate(x: int) -> int:
|
|
69
|
+
"""
|
|
70
|
+
>>> calculate(10)
|
|
71
|
+
100
|
|
72
|
+
"""
|
|
73
|
+
... # Implementation comes in BUILD
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3. BUILD
|
|
77
|
+
|
|
78
|
+
**For complex tasks:** Enter Plan Mode first, get user approval.
|
|
79
|
+
|
|
80
|
+
**Implementation rules:**
|
|
81
|
+
- Follow the contracts written in SPECIFY
|
|
82
|
+
{% if syntax == "mcp" -%}
|
|
83
|
+
- Run `invar_guard(changed=true)` frequently
|
|
84
|
+
{% else -%}
|
|
85
|
+
- Run `invar guard --changed` frequently
|
|
86
|
+
{% endif -%}
|
|
87
|
+
- Commit after each logical unit
|
|
88
|
+
|
|
89
|
+
**Commit format:**
|
|
90
|
+
```bash
|
|
91
|
+
git add . && git commit -m "feat: [description]
|
|
92
|
+
|
|
93
|
+
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
|
94
|
+
|
|
95
|
+
Co-Authored-By: Claude <noreply@anthropic.com>"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 4. VALIDATE
|
|
99
|
+
|
|
100
|
+
{% if syntax == "mcp" -%}
|
|
101
|
+
- Run `invar_guard()` (full verification)
|
|
102
|
+
{% else -%}
|
|
103
|
+
- Run `invar guard` (full verification)
|
|
104
|
+
{% endif -%}
|
|
105
|
+
- All TodoWrite items complete
|
|
106
|
+
- Integration works (if applicable)
|
|
107
|
+
|
|
108
|
+
## Task Batching
|
|
109
|
+
|
|
110
|
+
For multiple tasks:
|
|
111
|
+
1. Create TodoWrite with all items upfront
|
|
112
|
+
2. Execute sequentially (not parallel)
|
|
113
|
+
3. After each task:
|
|
114
|
+
- Commit changes
|
|
115
|
+
{% if syntax == "mcp" -%}
|
|
116
|
+
- Run `invar_guard(changed=true)`
|
|
117
|
+
{% else -%}
|
|
118
|
+
- Run `invar guard --changed`
|
|
119
|
+
{% endif -%}
|
|
120
|
+
- Update TodoWrite
|
|
121
|
+
4. **Limits:** Max 5 tasks OR 4 hours OR Guard failure
|
|
122
|
+
|
|
123
|
+
## Failure Handling
|
|
124
|
+
|
|
125
|
+
| Guard Result | Action |
|
|
126
|
+
|--------------|--------|
|
|
127
|
+
| Static fixable (missing contract) | Auto-fix, retry (max 2) |
|
|
128
|
+
| Test failure | Report to user, ask for guidance |
|
|
129
|
+
| Contract violation | Report, suggest `/investigate` |
|
|
130
|
+
| Repeated failure | Stop, ask user |
|
|
131
|
+
|
|
132
|
+
## Common Guard Errors
|
|
133
|
+
|
|
134
|
+
Quick reference for resolving common Guard errors:
|
|
135
|
+
|
|
136
|
+
| Error | Cause | Quick Fix |
|
|
137
|
+
|-------|-------|-----------|
|
|
138
|
+
| `forbidden_import: io` | I/O library in Core | Use `iter(s.splitlines())` not `io.StringIO` |
|
|
139
|
+
| `forbidden_import: os` | os module in Core | Accept `Path` as parameter instead |
|
|
140
|
+
| `forbidden_import: pathlib` | pathlib in Core | Accept `Path` or `str` as parameter |
|
|
141
|
+
| `internal_import` | Import inside function | Move import to module top |
|
|
142
|
+
| `missing_contract` | Core function without @pre/@post | Add contract before implementation |
|
|
143
|
+
| `empty_contract` | Contract with no condition | Add meaningful condition |
|
|
144
|
+
| `redundant_type_contract` | Contract only checks types | Add semantic constraints (bounds, relationships) |
|
|
145
|
+
| `partial_contract` | Only some params validated | Validate all params or document why partial |
|
|
146
|
+
| `file_size` | File > 500 lines | Extract functions to new module |
|
|
147
|
+
| `shell_result` | Shell function missing Result | Return `Result[T, E]` from `returns` |
|
|
148
|
+
|
|
149
|
+
**Tip:** For `missing_contract`, Guard automatically suggests contracts based on parameter types.
|
|
150
|
+
Check the "Suggested:" line in Guard output.
|
|
151
|
+
|
|
152
|
+
**Note:** Use `from deal import pre, post` for lambda-based contracts.
|
|
153
|
+
`invar_runtime.pre/post` are for Contract objects like `NonEmpty`.
|
|
154
|
+
|
|
155
|
+
## Timeout Handling
|
|
156
|
+
|
|
157
|
+
| Threshold | Duration | Action |
|
|
158
|
+
|-----------|----------|--------|
|
|
159
|
+
| Warning | 3 hours (75%) | Soft warning with options |
|
|
160
|
+
| Hard stop | 4 hours (max) | Save state, exit |
|
|
161
|
+
|
|
162
|
+
**75% Warning:**
|
|
163
|
+
```
|
|
164
|
+
⏱ Time check: /develop has been running for 3 hours.
|
|
165
|
+
Remaining estimate: [based on TodoWrite progress]
|
|
166
|
+
|
|
167
|
+
Options:
|
|
168
|
+
A: Continue (1 hour max remaining)
|
|
169
|
+
B: Wrap up current task and exit
|
|
170
|
+
C: Checkpoint and pause for later
|
|
171
|
+
|
|
172
|
+
Choice? (auto-continue in 2 minutes if no response)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Hard Stop:**
|
|
176
|
+
```
|
|
177
|
+
⏱ /develop reached 4-hour limit.
|
|
178
|
+
|
|
179
|
+
Completed: [N]/[M] tasks
|
|
180
|
+
Current task: [description] - [%] complete
|
|
181
|
+
|
|
182
|
+
Saving state for resume. Run '/develop --resume' to continue.
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Exit Actions (REQUIRED)
|
|
186
|
+
|
|
187
|
+
### Final
|
|
188
|
+
|
|
189
|
+
{% if syntax == "mcp" -%}
|
|
190
|
+
```python
|
|
191
|
+
invar_guard()
|
|
192
|
+
```
|
|
193
|
+
{% else -%}
|
|
194
|
+
```bash
|
|
195
|
+
invar guard
|
|
196
|
+
```
|
|
197
|
+
{% endif %}
|
|
198
|
+
|
|
199
|
+
**Display:**
|
|
200
|
+
```
|
|
201
|
+
✓ Final: guard [PASS/FAIL] | [errors] errors, [warnings] warnings
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Auto-Review (DX-41)
|
|
205
|
+
|
|
206
|
+
If Guard outputs `review_suggested`:
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
⚠ review_suggested: [reason]
|
|
210
|
+
|
|
211
|
+
📍 Routing: /review — review_suggested triggered
|
|
212
|
+
Task: Review [N files changed]
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Proceed directly to /review skill. User can say "skip" to bypass.
|
|
216
|
+
|
|
217
|
+
## Phase Visibility (DX-51)
|
|
218
|
+
|
|
219
|
+
**USBV phases must be visually distinct.** On each phase transition, display a phase header:
|
|
220
|
+
|
|
221
|
+
### Phase Header Format
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
225
|
+
📍 /develop → SPECIFY (2/4)
|
|
226
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Compact Format (brief updates)
|
|
230
|
+
|
|
231
|
+
```
|
|
232
|
+
📍 VALIDATE — Running guard...
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Three-Layer Visibility
|
|
236
|
+
|
|
237
|
+
| Layer | What | Tool |
|
|
238
|
+
|-------|------|------|
|
|
239
|
+
| Skill | `/develop` | Routing announcement |
|
|
240
|
+
| Phase | `SPECIFY (2/4)` | Phase header (this section) |
|
|
241
|
+
| Tasks | Concrete items | TodoWrite |
|
|
242
|
+
|
|
243
|
+
**Phase headers are SEPARATE from TodoWrite.**
|
|
244
|
+
- Phase = where you are in workflow (visible in output)
|
|
245
|
+
- TodoWrite = what tasks need doing (visible in status panel)
|
|
246
|
+
|
|
247
|
+
**BUILD is internal work** — show header but no detailed breakdown.
|
|
248
|
+
|
|
249
|
+
## Tool Selection
|
|
250
|
+
|
|
251
|
+
| I want to... | Use |
|
|
252
|
+
|--------------|-----|
|
|
253
|
+
{% if syntax == "mcp" -%}
|
|
254
|
+
| See contracts | `invar_sig <file>` |
|
|
255
|
+
| Find entry points | `invar_map --top 10` |
|
|
256
|
+
| Verify code | `invar_guard` |
|
|
257
|
+
{% else -%}
|
|
258
|
+
| See contracts | `invar sig <file>` |
|
|
259
|
+
| Find entry points | `invar map --top 10` |
|
|
260
|
+
| Verify code | `invar guard` |
|
|
261
|
+
{% endif -%}
|
|
262
|
+
| Edit symbol | Serena `replace_symbol_body` |
|
|
263
|
+
| Add after symbol | Serena `insert_after_symbol` |
|
|
264
|
+
| Rename symbol | Serena `rename_symbol` |
|
|
265
|
+
|
|
266
|
+
## Example
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
User: "Add input validation to parse_source"
|
|
270
|
+
|
|
271
|
+
Agent:
|
|
272
|
+
📍 Routing: /develop — "add" trigger detected
|
|
273
|
+
Task: Add input validation to parse_source
|
|
274
|
+
|
|
275
|
+
✓ Check-In: Invar | main | clean
|
|
276
|
+
|
|
277
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
278
|
+
📍 /develop → UNDERSTAND (1/4)
|
|
279
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
280
|
+
|
|
281
|
+
- Current: accepts any string
|
|
282
|
+
- Need: reject whitespace-only strings
|
|
283
|
+
- File: src/invar/core/parser.py
|
|
284
|
+
|
|
285
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
286
|
+
📍 /develop → SPECIFY (2/4)
|
|
287
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
288
|
+
|
|
289
|
+
@pre(lambda source, path: len(source.strip()) > 0)
|
|
290
|
+
|
|
291
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
292
|
+
📍 /develop → BUILD (3/4)
|
|
293
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
294
|
+
|
|
295
|
+
[Implementation...]
|
|
296
|
+
|
|
297
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
298
|
+
📍 /develop → VALIDATE (4/4)
|
|
299
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
300
|
+
|
|
301
|
+
✓ guard PASS | 0 errors, 1 warning
|
|
302
|
+
|
|
303
|
+
✓ Final: guard PASS | 0 errors, 1 warning
|
|
304
|
+
```
|
|
305
|
+
<!--/invar:skill-->
|
|
306
|
+
|
|
307
|
+
<!--invar:extensions-->
|
|
308
|
+
<!-- ========================================================================
|
|
309
|
+
EXTENSIONS REGION - USER EDITABLE
|
|
310
|
+
Add project-specific extensions here. This section is preserved on update.
|
|
311
|
+
|
|
312
|
+
Examples of what to add:
|
|
313
|
+
- Project-specific validation steps
|
|
314
|
+
- Custom commit message formats
|
|
315
|
+
- Additional tool integrations
|
|
316
|
+
- Team-specific workflows
|
|
317
|
+
======================================================================== -->
|
|
318
|
+
<!--/invar:extensions-->
|