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.
Files changed (89) hide show
  1. invar/__init__.py +1 -0
  2. invar/core/contracts.py +10 -10
  3. invar/core/entry_points.py +105 -32
  4. invar/core/extraction.py +5 -6
  5. invar/core/format_specs.py +1 -2
  6. invar/core/formatter.py +6 -7
  7. invar/core/hypothesis_strategies.py +5 -7
  8. invar/core/inspect.py +1 -1
  9. invar/core/lambda_helpers.py +3 -3
  10. invar/core/models.py +7 -1
  11. invar/core/must_use.py +2 -1
  12. invar/core/parser.py +7 -4
  13. invar/core/postcondition_scope.py +128 -0
  14. invar/core/property_gen.py +8 -5
  15. invar/core/purity.py +3 -3
  16. invar/core/purity_heuristics.py +5 -9
  17. invar/core/references.py +8 -6
  18. invar/core/review_trigger.py +78 -6
  19. invar/core/rule_meta.py +8 -0
  20. invar/core/rules.py +18 -19
  21. invar/core/shell_analysis.py +5 -10
  22. invar/core/shell_architecture.py +2 -2
  23. invar/core/strategies.py +7 -14
  24. invar/core/suggestions.py +86 -0
  25. invar/core/sync_helpers.py +238 -0
  26. invar/core/tautology.py +102 -37
  27. invar/core/template_parser.py +467 -0
  28. invar/core/timeout_inference.py +4 -7
  29. invar/core/utils.py +13 -15
  30. invar/core/verification_routing.py +4 -7
  31. invar/mcp/server.py +100 -17
  32. invar/shell/commands/__init__.py +11 -0
  33. invar/shell/{cli.py → commands/guard.py} +94 -14
  34. invar/shell/{init_cmd.py → commands/init.py} +179 -27
  35. invar/shell/commands/merge.py +256 -0
  36. invar/shell/commands/sync_self.py +113 -0
  37. invar/shell/commands/template_sync.py +366 -0
  38. invar/shell/commands/update.py +48 -0
  39. invar/shell/config.py +12 -24
  40. invar/shell/coverage.py +351 -0
  41. invar/shell/guard_helpers.py +38 -17
  42. invar/shell/guard_output.py +7 -1
  43. invar/shell/property_tests.py +58 -22
  44. invar/shell/prove/__init__.py +9 -0
  45. invar/shell/{prove.py → prove/crosshair.py} +40 -33
  46. invar/shell/{prove_fallback.py → prove/hypothesis.py} +12 -4
  47. invar/shell/subprocess_env.py +393 -0
  48. invar/shell/template_engine.py +345 -0
  49. invar/shell/templates.py +19 -0
  50. invar/shell/testing.py +71 -20
  51. invar/templates/CLAUDE.md.template +38 -17
  52. invar/templates/aider.conf.yml.template +2 -2
  53. invar/templates/commands/{review.md → audit.md} +20 -82
  54. invar/templates/commands/guard.md +77 -0
  55. invar/templates/config/CLAUDE.md.jinja +206 -0
  56. invar/templates/config/context.md.jinja +92 -0
  57. invar/templates/config/pre-commit.yaml.jinja +44 -0
  58. invar/templates/context.md.template +33 -0
  59. invar/templates/cursorrules.template +7 -4
  60. invar/templates/examples/README.md +2 -0
  61. invar/templates/examples/conftest.py +3 -0
  62. invar/templates/examples/contracts.py +5 -5
  63. invar/templates/examples/core_shell.py +11 -7
  64. invar/templates/examples/workflow.md +81 -0
  65. invar/templates/manifest.toml +137 -0
  66. invar/templates/{INVAR.md → protocol/INVAR.md} +10 -7
  67. invar/templates/skills/develop/SKILL.md.jinja +318 -0
  68. invar/templates/skills/investigate/SKILL.md.jinja +106 -0
  69. invar/templates/skills/propose/SKILL.md.jinja +104 -0
  70. invar/templates/skills/review/SKILL.md.jinja +125 -0
  71. {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/METADATA +108 -118
  72. invar_tools-1.3.0.dist-info/RECORD +95 -0
  73. invar_tools-1.3.0.dist-info/entry_points.txt +2 -0
  74. invar/contracts.py +0 -152
  75. invar/decorators.py +0 -94
  76. invar/invariant.py +0 -58
  77. invar/resource.py +0 -99
  78. invar/shell/update_cmd.py +0 -193
  79. invar_tools-1.2.0.dist-info/RECORD +0 -77
  80. invar_tools-1.2.0.dist-info/entry_points.txt +0 -2
  81. /invar/shell/{mutate_cmd.py → commands/mutate.py} +0 -0
  82. /invar/shell/{perception.py → commands/perception.py} +0 -0
  83. /invar/shell/{test_cmd.py → commands/test.py} +0 -0
  84. /invar/shell/{prove_accept.py → prove/accept.py} +0 -0
  85. /invar/shell/{prove_cache.py → prove/cache.py} +0 -0
  86. {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/WHEEL +0 -0
  87. {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/licenses/LICENSE +0 -0
  88. {invar_tools-1.2.0.dist-info → invar_tools-1.3.0.dist-info}/licenses/LICENSE-GPL +0 -0
  89. {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
- # Use invar_runtime for lightweight runtime contracts
11
- # (or 'from deal import pre, post' works too - deal is the underlying library)
12
- from invar_runtime import post, pre
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
- @pre(lambda content: isinstance(content, str))
24
- @post(lambda result: isinstance(result, list))
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
- @pre(lambda items: isinstance(items, list))
40
- @post(lambda result: isinstance(result, dict))
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 v4.0
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: guard PASS | top: <entry1>, <entry2>
84
+ ✓ Check-In: [project] | [branch] | [clean/dirty]
85
85
  ```
86
86
 
87
- Execute `invar_guard(changed=true)` and `invar_map(top=10)`, then show this one-line summary.
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: guard PASS | top: <entry1>, <entry2>`
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 v4.0 — USBV workflow (DX-32) | [Guide](docs/INVAR-GUIDE.md) | [Examples](.invar/examples/)*
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-->