invar-tools 1.7.1__py3-none-any.whl → 1.10.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/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/template_helpers.py +32 -0
- invar/core/ts_parsers.py +286 -0
- invar/core/ts_sig_parser.py +307 -0
- 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/guard.py +36 -1
- invar/shell/commands/init.py +133 -7
- 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 +77 -12
- invar/shell/commands/update.py +6 -14
- invar/shell/contract_coverage.py +1 -0
- invar/shell/fs.py +66 -13
- invar/shell/pi_hooks.py +213 -0
- invar/shell/prove/guard_ts.py +899 -0
- invar/shell/skill_manager.py +353 -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 +256 -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 +82 -0
- invar/templates/manifest.toml +8 -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 +98 -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/investigate/SKILL.md.jinja +15 -0
- invar/templates/skills/propose/SKILL.md.jinja +33 -0
- invar/templates/skills/review/SKILL.md.jinja +346 -71
- {invar_tools-1.7.1.dist-info → invar_tools-1.10.0.dist-info}/METADATA +326 -19
- invar_tools-1.10.0.dist-info/RECORD +173 -0
- invar/templates/examples/core_shell.py +0 -127
- invar/templates/protocol/INVAR.md +0 -310
- invar_tools-1.7.1.dist-info/RECORD +0 -112
- /invar/templates/examples/{workflow.md → python/workflow.md} +0 -0
- {invar_tools-1.7.1.dist-info → invar_tools-1.10.0.dist-info}/WHEEL +0 -0
- {invar_tools-1.7.1.dist-info → invar_tools-1.10.0.dist-info}/entry_points.txt +0 -0
- {invar_tools-1.7.1.dist-info → invar_tools-1.10.0.dist-info}/licenses/LICENSE +0 -0
- {invar_tools-1.7.1.dist-info → invar_tools-1.10.0.dist-info}/licenses/LICENSE-GPL +0 -0
- {invar_tools-1.7.1.dist-info → invar_tools-1.10.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
## Contract Syntax (Python)
|
|
2
|
+
|
|
3
|
+
### Lambda Signature (Critical)
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
# WRONG: Lambda only takes first parameter
|
|
7
|
+
@pre(lambda x: x >= 0)
|
|
8
|
+
def calculate(x: int, y: int = 0): ...
|
|
9
|
+
|
|
10
|
+
# CORRECT: Lambda must include ALL parameters (even defaults)
|
|
11
|
+
@pre(lambda x, y=0: x >= 0)
|
|
12
|
+
def calculate(x: int, y: int = 0): ...
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Guard's `param_mismatch` rule catches this as ERROR.
|
|
16
|
+
|
|
17
|
+
### Meaningful Contracts
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
# Redundant - type hints already check this
|
|
21
|
+
@pre(lambda x: isinstance(x, int))
|
|
22
|
+
def calc(x: int): ...
|
|
23
|
+
|
|
24
|
+
# Meaningful - checks business logic
|
|
25
|
+
@pre(lambda x: x > 0)
|
|
26
|
+
def calc(x: int): ...
|
|
27
|
+
|
|
28
|
+
# Meaningful - checks relationship between params
|
|
29
|
+
@pre(lambda start, end: start < end)
|
|
30
|
+
def process_range(start: int, end: int): ...
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### @post Scope
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
# WRONG: @post cannot access function parameters
|
|
37
|
+
@post(lambda result: result > x) # 'x' not available!
|
|
38
|
+
def calc(x: int) -> int: ...
|
|
39
|
+
|
|
40
|
+
# CORRECT: @post can only use 'result'
|
|
41
|
+
@post(lambda result: result >= 0)
|
|
42
|
+
def calc(x: int) -> int: ...
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Doctest Examples
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
def calculate(x: int) -> int:
|
|
49
|
+
"""
|
|
50
|
+
>>> calculate(5)
|
|
51
|
+
10
|
|
52
|
+
>>> calculate(0) # Edge case
|
|
53
|
+
0
|
|
54
|
+
"""
|
|
55
|
+
return x * 2
|
|
56
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
## Markers (Python)
|
|
2
|
+
|
|
3
|
+
### Entry Points
|
|
4
|
+
|
|
5
|
+
Entry points are framework callbacks (`@app.route`, `@app.command`) at Shell boundary.
|
|
6
|
+
- **Exempt** from `Result[T, E]` — must match framework signature
|
|
7
|
+
- **Keep thin** (max 15 lines) — delegate to Shell functions that return Result
|
|
8
|
+
|
|
9
|
+
Auto-detected by decorators. For custom callbacks:
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
# @shell:entry
|
|
13
|
+
def on_custom_event(data: dict) -> dict:
|
|
14
|
+
result = handle_event(data)
|
|
15
|
+
return result.unwrap_or({"error": "failed"})
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Shell Complexity
|
|
19
|
+
|
|
20
|
+
When shell function complexity is justified:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
# @shell_complexity: Subprocess with error classification
|
|
24
|
+
def run_external_tool(...): ...
|
|
25
|
+
|
|
26
|
+
# @shell_orchestration: Multi-step pipeline coordination
|
|
27
|
+
def process_batch(...): ...
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Architecture Escape Hatch
|
|
31
|
+
|
|
32
|
+
When rule violation has valid architectural justification:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
# @invar:allow shell_result: Framework callback signature fixed
|
|
36
|
+
def flask_handler(): ...
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Valid rule names for @invar:allow:**
|
|
40
|
+
- `shell_result` — Shell function without Result return type
|
|
41
|
+
- `entry_point_too_thick` — Entry point exceeds 15 lines
|
|
42
|
+
- `forbidden_import` — I/O import in Core (rare, justify carefully)
|
|
43
|
+
|
|
44
|
+
Run `invar rules` for complete rule catalog with hints.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
## Commands (Python)
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
invar guard # Full: static + doctests + CrossHair + Hypothesis
|
|
5
|
+
invar guard --static # Static only (quick debug, ~0.5s)
|
|
6
|
+
invar guard --changed # Modified files only
|
|
7
|
+
invar guard --coverage # Collect branch coverage
|
|
8
|
+
invar guard -c # Contract coverage only (DX-63)
|
|
9
|
+
invar sig <file> # Show contracts + signatures
|
|
10
|
+
invar map --top 10 # Most-referenced symbols
|
|
11
|
+
invar rules # List all rules with detection/hints (JSON)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Configuration (Python)
|
|
15
|
+
|
|
16
|
+
```toml
|
|
17
|
+
# pyproject.toml or invar.toml
|
|
18
|
+
[tool.invar.guard]
|
|
19
|
+
core_paths = ["src/myapp/core"] # Default: ["src/core", "core"]
|
|
20
|
+
shell_paths = ["src/myapp/shell"] # Default: ["src/shell", "shell"]
|
|
21
|
+
max_file_lines = 500 # Default: 500 (warning at 80%)
|
|
22
|
+
max_function_lines = 50 # Default: 50
|
|
23
|
+
# Doctest lines are excluded from size calculations
|
|
24
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
## Troubleshooting (Python)
|
|
2
|
+
|
|
3
|
+
### Size Limits (Agent Quick Reference)
|
|
4
|
+
|
|
5
|
+
| Rule | Limit | Fix |
|
|
6
|
+
|------|-------|-----|
|
|
7
|
+
| `function_too_long` | **50 lines** | Extract helper: `_impl()` + main with docstring |
|
|
8
|
+
| `file_too_long` | **500 lines** | Split by responsibility |
|
|
9
|
+
| `entry_point_too_thick` | **15 lines** | Delegate to Shell functions |
|
|
10
|
+
|
|
11
|
+
*Doctest lines excluded from counts. Limits configurable in `pyproject.toml`.*
|
|
12
|
+
|
|
13
|
+
### Common Errors
|
|
14
|
+
|
|
15
|
+
| Symptom | Cause | Fix |
|
|
16
|
+
|---------|-------|-----|
|
|
17
|
+
| `param_mismatch` error | Lambda missing params | Include ALL params (even defaults) |
|
|
18
|
+
| `shell_result` error | Shell func no Result | Add Result[T,E] or @invar:allow |
|
|
19
|
+
| `is_failure()` not found | Wrong Result check | Use `isinstance(result, Failure)` |
|
|
20
|
+
|
|
21
|
+
### Result Type Usage
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from returns.result import Result, Success, Failure
|
|
25
|
+
|
|
26
|
+
# Creating results
|
|
27
|
+
return Success(value)
|
|
28
|
+
return Failure(error)
|
|
29
|
+
|
|
30
|
+
# Checking results
|
|
31
|
+
if isinstance(result, Failure):
|
|
32
|
+
handle_error(result.failure())
|
|
33
|
+
else:
|
|
34
|
+
use_value(result.unwrap())
|
|
35
|
+
|
|
36
|
+
# Chaining
|
|
37
|
+
result.map(transform).bind(next_operation)
|
|
38
|
+
```
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
## Core Example (TypeScript)
|
|
2
|
+
|
|
3
|
+
```typescript
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
// Contracts as Zod schemas
|
|
7
|
+
const DiscountInput = z.object({
|
|
8
|
+
price: z.number().positive(),
|
|
9
|
+
discount: z.number().min(0).max(1),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const DiscountOutput = z.number().nonnegative();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @example discountedPrice({ price: 100, discount: 0.2 }) // => 80
|
|
16
|
+
* @example discountedPrice({ price: 100, discount: 0 }) // => 100 (edge: no discount)
|
|
17
|
+
*/
|
|
18
|
+
export function discountedPrice(input: z.infer<typeof DiscountInput>): number {
|
|
19
|
+
const { price, discount } = DiscountInput.parse(input);
|
|
20
|
+
const result = price * (1 - discount);
|
|
21
|
+
return DiscountOutput.parse(result);
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Self-test:** Can someone else write the exact same function from just Zod schemas + @example?
|
|
26
|
+
|
|
27
|
+
**Forbidden in Core:** `fs`, `path`, `http`, `fetch`, `process.env`, `Date.now()`
|
|
28
|
+
|
|
29
|
+
## Shell Example (TypeScript)
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { Result, ok, err } from 'neverthrow';
|
|
33
|
+
import { readFileSync } from 'fs';
|
|
34
|
+
|
|
35
|
+
interface Config { [key: string]: unknown }
|
|
36
|
+
|
|
37
|
+
export function readConfig(path: string): Result<Config, string> {
|
|
38
|
+
try {
|
|
39
|
+
const content = readFileSync(path, 'utf-8');
|
|
40
|
+
return ok(JSON.parse(content));
|
|
41
|
+
} catch (e) {
|
|
42
|
+
if (e instanceof Error && 'code' in e && e.code === 'ENOENT') {
|
|
43
|
+
return err(`File not found: ${path}`);
|
|
44
|
+
}
|
|
45
|
+
return err(`Invalid JSON: ${e}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Pattern:** Shell reads file → passes content to Core → returns Result.
|
|
51
|
+
|
|
52
|
+
More examples: `.invar/examples/`
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
## Contract Syntax (TypeScript)
|
|
2
|
+
|
|
3
|
+
### Zod Schema Patterns
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
|
|
8
|
+
// Input validation (precondition)
|
|
9
|
+
const CalcInput = z.object({
|
|
10
|
+
x: z.number().int().nonnegative(),
|
|
11
|
+
y: z.number().int().default(0),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// Output validation (postcondition)
|
|
15
|
+
const CalcOutput = z.number().int().nonnegative();
|
|
16
|
+
|
|
17
|
+
function calculate(input: z.infer<typeof CalcInput>): number {
|
|
18
|
+
const { x, y } = CalcInput.parse(input);
|
|
19
|
+
const result = x + y;
|
|
20
|
+
return CalcOutput.parse(result);
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Meaningful Contracts
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// Redundant - TypeScript already checks this
|
|
28
|
+
const BadSchema = z.object({
|
|
29
|
+
x: z.number(), // Just type checking
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Meaningful - checks business logic
|
|
33
|
+
const GoodSchema = z.object({
|
|
34
|
+
x: z.number().positive(), // Domain constraint
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Meaningful - checks relationship
|
|
38
|
+
const RangeInput = z.object({
|
|
39
|
+
start: z.number(),
|
|
40
|
+
end: z.number(),
|
|
41
|
+
}).refine(data => data.start < data.end, {
|
|
42
|
+
message: "start must be less than end",
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Postcondition Scope
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// Output schema can only validate the result
|
|
50
|
+
const OutputSchema = z.object({
|
|
51
|
+
total: z.number().nonnegative(),
|
|
52
|
+
items: z.array(z.string()).min(1),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// For input-dependent validation, use refinement in function
|
|
56
|
+
function process(items: string[]): z.infer<typeof OutputSchema> {
|
|
57
|
+
const result = { total: items.length, items };
|
|
58
|
+
return OutputSchema.parse(result);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### JSDoc Examples
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
/**
|
|
66
|
+
* Calculate doubled value.
|
|
67
|
+
* @example calculate(5) // => 10
|
|
68
|
+
* @example calculate(0) // => 0 (edge case)
|
|
69
|
+
*/
|
|
70
|
+
function calculate(x: number): number {
|
|
71
|
+
return x * 2;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
## Markers (TypeScript)
|
|
2
|
+
|
|
3
|
+
### Entry Points
|
|
4
|
+
|
|
5
|
+
Entry points are framework callbacks (Express routes, Next.js handlers) at Shell boundary.
|
|
6
|
+
- **Exempt** from `Result<T, E>` — must match framework signature
|
|
7
|
+
- **Keep thin** (max 15 lines) — delegate to Shell functions that return Result
|
|
8
|
+
|
|
9
|
+
For custom callbacks:
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// @shell:entry
|
|
13
|
+
export function onCustomEvent(data: Record<string, unknown>): Response {
|
|
14
|
+
const result = handleEvent(data);
|
|
15
|
+
if (result.isErr()) {
|
|
16
|
+
return new Response(JSON.stringify({ error: result.error }), { status: 500 });
|
|
17
|
+
}
|
|
18
|
+
return new Response(JSON.stringify(result.value));
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Shell Complexity
|
|
23
|
+
|
|
24
|
+
When shell function complexity is justified:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// @shell_complexity: External process with error classification
|
|
28
|
+
async function runExternalTool(...): Promise<Result<Output, Error>> { ... }
|
|
29
|
+
|
|
30
|
+
// @shell_orchestration: Multi-step pipeline coordination
|
|
31
|
+
async function processBatch(...): Promise<Result<BatchResult, Error>> { ... }
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Architecture Escape Hatch
|
|
35
|
+
|
|
36
|
+
When rule violation has valid architectural justification:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// @invar:allow shell_result: Express middleware signature fixed
|
|
40
|
+
function expressMiddleware(req: Request, res: Response, next: NextFunction): void { ... }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Valid rule names for @invar:allow:**
|
|
44
|
+
- `shell_result` — Shell function without Result return type
|
|
45
|
+
- `entry_point_too_thick` — Entry point exceeds 15 lines
|
|
46
|
+
- `forbidden_import` — I/O import in Core (rare, justify carefully)
|
|
47
|
+
|
|
48
|
+
Run `invar rules` for complete rule catalog with hints.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
## Commands (TypeScript)
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
# Verification (Python CLI - works for TypeScript)
|
|
5
|
+
invar guard # Full: tsc + eslint + vitest + ts-analyzer
|
|
6
|
+
invar guard --json # Agent-friendly v2.0 JSON output
|
|
7
|
+
invar guard --changed # Modified files only
|
|
8
|
+
|
|
9
|
+
# Analysis
|
|
10
|
+
invar sig <file> # Show function signatures
|
|
11
|
+
invar map --top 10 # Most-referenced symbols
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Guard Output (v2.0 JSON)
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"version": "2.0",
|
|
19
|
+
"language": "typescript",
|
|
20
|
+
"status": "passed",
|
|
21
|
+
"contracts": {
|
|
22
|
+
"coverage": {"total": 10, "withContracts": 7, "percent": 70},
|
|
23
|
+
"quality": {"strong": 3, "medium": 2, "weak": 1, "useless": 0},
|
|
24
|
+
"blind_spots": [
|
|
25
|
+
{"function": "deleteUser", "risk": "high", "suggested_schema": "z.object({...})"}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Configuration (TypeScript)
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
// package.json
|
|
35
|
+
{
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"lint": "eslint src/"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
// vitest.config.ts
|
|
46
|
+
import { defineConfig } from 'vitest/config';
|
|
47
|
+
|
|
48
|
+
export default defineConfig({
|
|
49
|
+
test: {
|
|
50
|
+
include: ['src/**/*.test.ts', 'src/**/*.property.ts'],
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Embedded Node Tools
|
|
56
|
+
|
|
57
|
+
Invar includes bundled TypeScript analysis tools (no npm install required):
|
|
58
|
+
|
|
59
|
+
| Tool | Purpose |
|
|
60
|
+
|------|---------|
|
|
61
|
+
| **ts-analyzer** | Contract coverage, blind spot detection |
|
|
62
|
+
| **fc-runner** | Property-based testing with fast-check |
|
|
63
|
+
| **quick-check** | Fast pre-commit verification (<1s) |
|
|
64
|
+
|
|
65
|
+
These are called automatically by `invar guard` when analyzing TypeScript projects.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
## Troubleshooting (TypeScript)
|
|
2
|
+
|
|
3
|
+
### Guard Output Interpretation
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{
|
|
7
|
+
"status": "failed",
|
|
8
|
+
"contracts": {
|
|
9
|
+
"coverage": {"total": 10, "withContracts": 5, "percent": 50},
|
|
10
|
+
"blind_spots": [
|
|
11
|
+
{"function": "deleteUser", "risk": "high", "suggested_schema": "z.object({userId: z.string()})"}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
| Field | Meaning | Action |
|
|
18
|
+
|-------|---------|--------|
|
|
19
|
+
| `status: failed` | Verification errors | Fix tsc/eslint/test errors first |
|
|
20
|
+
| `coverage.percent < 70` | Low contract coverage | Add Zod schemas to uncovered functions |
|
|
21
|
+
| `blind_spots` (high risk) | Critical functions without contracts | Priority: add schemas before next commit |
|
|
22
|
+
| `blind_spots` (medium risk) | Functions that should have contracts | Add schemas when modifying |
|
|
23
|
+
|
|
24
|
+
### Fixing Blind Spots
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// Before: blind spot (no validation)
|
|
28
|
+
function deleteUser(userId: string): void {
|
|
29
|
+
db.delete(userId);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// After: contract added
|
|
33
|
+
const DeleteUserInput = z.object({
|
|
34
|
+
userId: z.string().uuid(),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
function deleteUser(input: z.infer<typeof DeleteUserInput>): void {
|
|
38
|
+
const { userId } = DeleteUserInput.parse(input);
|
|
39
|
+
db.delete(userId);
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Size Limits (Agent Quick Reference)
|
|
44
|
+
|
|
45
|
+
| Rule | Limit | Fix |
|
|
46
|
+
|------|-------|-----|
|
|
47
|
+
| `function_too_long` | **50 lines** | Extract helper: `_impl()` + main with JSDoc |
|
|
48
|
+
| `file_too_long` | **500 lines** | Split by responsibility |
|
|
49
|
+
| `entry_point_too_thick` | **15 lines** | Delegate to Shell functions |
|
|
50
|
+
|
|
51
|
+
*JSDoc/comment lines excluded from counts.*
|
|
52
|
+
|
|
53
|
+
### Common Errors
|
|
54
|
+
|
|
55
|
+
| Symptom | Cause | Fix |
|
|
56
|
+
|---------|-------|-----|
|
|
57
|
+
| `ZodError` at runtime | Schema mismatch | Check input against Zod schema |
|
|
58
|
+
| `shell_result` error | Shell func no Result | Add Result<T,E> or @invar:allow |
|
|
59
|
+
| `isErr()` not found | Wrong Result import | Use `neverthrow` Result type |
|
|
60
|
+
|
|
61
|
+
### Result Type Usage
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { Result, ok, err } from 'neverthrow';
|
|
65
|
+
|
|
66
|
+
// Creating results
|
|
67
|
+
return ok(value);
|
|
68
|
+
return err(error);
|
|
69
|
+
|
|
70
|
+
// Checking results
|
|
71
|
+
if (result.isErr()) {
|
|
72
|
+
handleError(result.error);
|
|
73
|
+
} else {
|
|
74
|
+
useValue(result.value);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Chaining
|
|
78
|
+
result
|
|
79
|
+
.map(transform)
|
|
80
|
+
.andThen(nextOperation);
|
|
81
|
+
|
|
82
|
+
// Async operations
|
|
83
|
+
import { ResultAsync } from 'neverthrow';
|
|
84
|
+
|
|
85
|
+
const asyncResult = ResultAsync.fromPromise(
|
|
86
|
+
fetch(url),
|
|
87
|
+
(e) => new Error(`Fetch failed: ${e}`)
|
|
88
|
+
);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Zod Validation Patterns
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { z } from 'zod';
|
|
95
|
+
|
|
96
|
+
// Safe parse (returns Result-like object)
|
|
97
|
+
const parsed = Schema.safeParse(data);
|
|
98
|
+
if (!parsed.success) {
|
|
99
|
+
console.error(parsed.error.issues);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Strict parse (throws on error)
|
|
103
|
+
const validated = Schema.parse(data);
|
|
104
|
+
```
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
## Core/Shell Architecture
|
|
2
|
+
|
|
3
|
+
| Zone | Location | Requirements |
|
|
4
|
+
|------|----------|--------------|
|
|
5
|
+
| Core | `**/core/**` | Contracts + Examples, pure (no I/O) |
|
|
6
|
+
| Shell | `**/shell/**` | Error-handling return type |
|
|
7
|
+
|
|
8
|
+
### Decision Tree: Core vs Shell
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
Does this function...
|
|
12
|
+
│
|
|
13
|
+
├─ Read or write files? ──────────────────→ Shell
|
|
14
|
+
├─ Make network requests? ─────────────────→ Shell
|
|
15
|
+
├─ Access current time? ──────────────────→ Shell OR inject as parameter
|
|
16
|
+
├─ Generate random values? ────────────────→ Shell OR inject as parameter
|
|
17
|
+
├─ Print to console? ──────────────────────→ Shell (return data, Shell logs)
|
|
18
|
+
├─ Access environment variables? ──────────→ Shell
|
|
19
|
+
│
|
|
20
|
+
└─ None of the above? ─────────────────────→ Core
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Injection Pattern (Universal)
|
|
24
|
+
|
|
25
|
+
Instead of accessing impure values directly, inject them as parameters:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
# Core: receives 'current_time' as parameter (pure)
|
|
29
|
+
FUNCTION is_expired(expiry, current_time):
|
|
30
|
+
RETURN current_time > expiry
|
|
31
|
+
|
|
32
|
+
# Shell: calls with actual time
|
|
33
|
+
expired = is_expired(token.expiry, get_current_time())
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This keeps Core functions pure and testable.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
## Task Completion
|
|
2
|
+
|
|
3
|
+
A task is complete only when ALL conditions are met:
|
|
4
|
+
- Check-In displayed: `✓ Check-In: [project] | [branch] | [clean/dirty]`
|
|
5
|
+
- Intent explicitly stated
|
|
6
|
+
- Contract written before implementation
|
|
7
|
+
- Final displayed: `✓ Final: verification PASS | <errors>, <warnings>`
|
|
8
|
+
- User requirement satisfied
|
|
9
|
+
|
|
10
|
+
**Missing any = Task incomplete.**
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
*Protocol v5.0 — USBV workflow | [Examples](.invar/examples/)*
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
## Contract Concepts
|
|
2
|
+
|
|
3
|
+
### Precondition
|
|
4
|
+
Constraints on inputs that must be true before function executes.
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
PRECONDITION: input_value > 0 AND input_value < 100
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
### Postcondition
|
|
11
|
+
Guarantees about outputs that must be true after function executes.
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
POSTCONDITION: result >= 0
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Self-Test Rule
|
|
18
|
+
|
|
19
|
+
> "Can someone else write the exact same function from just the contracts + examples?"
|
|
20
|
+
|
|
21
|
+
If yes → Contracts are complete.
|
|
22
|
+
If no → Add more constraints or examples.
|
|
23
|
+
|
|
24
|
+
### Meaningful Contracts
|
|
25
|
+
|
|
26
|
+
Contracts should check **business logic**, not just types:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
# Redundant - type system already checks this
|
|
30
|
+
PRECONDITION: x is integer
|
|
31
|
+
|
|
32
|
+
# Meaningful - checks business logic
|
|
33
|
+
PRECONDITION: x > 0
|
|
34
|
+
|
|
35
|
+
# Meaningful - checks relationship between params
|
|
36
|
+
PRECONDITION: start < end
|
|
37
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
3
|
+
│ INVAR-MANAGED FILE - DO NOT EDIT DIRECTLY │
|
|
4
|
+
│ │
|
|
5
|
+
│ This file is managed by Invar. Changes may be lost on │
|
|
6
|
+
│ `invar update`. Add project content to CLAUDE.md instead. │
|
|
7
|
+
└─────────────────────────────────────────────────────────────┘
|
|
8
|
+
|
|
9
|
+
License: CC-BY-4.0 (Creative Commons Attribution 4.0 International)
|
|
10
|
+
https://creativecommons.org/licenses/by/4.0/
|
|
11
|
+
|
|
12
|
+
You are free to share and adapt this document, provided you give
|
|
13
|
+
appropriate credit to the Invar project.
|
|
14
|
+
-->
|
|
15
|
+
# The Invar Protocol v5.0
|
|
16
|
+
|
|
17
|
+
> **"Trade structure for safety."**
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
## Check-In (Required)
|
|
2
|
+
|
|
3
|
+
Your first message MUST display:
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
✓ Check-In: [project] | [branch] | [clean/dirty]
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Actions:
|
|
10
|
+
1. Read `.invar/context.md` (Key Rules + Current State + Lessons Learned)
|
|
11
|
+
2. Show one-line status
|
|
12
|
+
|
|
13
|
+
**Do NOT execute verification at Check-In.**
|
|
14
|
+
Verification is for VALIDATE phase and Final only.
|
|
15
|
+
|
|
16
|
+
This is your sign-in. The user sees it immediately.
|
|
17
|
+
No visible check-in = Session not started.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
## Six Laws
|
|
2
|
+
|
|
3
|
+
| Law | Principle |
|
|
4
|
+
|-----|-----------|
|
|
5
|
+
| 1. Separation | Core (pure logic) / Shell (I/O) physically separate |
|
|
6
|
+
| 2. Contract Complete | Preconditions + Postconditions + Examples uniquely determine implementation |
|
|
7
|
+
| 3. Context Economy | Overview → Signatures → Code (only read what's needed) |
|
|
8
|
+
| 4. Decompose First | Break into sub-functions before implementing |
|
|
9
|
+
| 5. Verify Reflectively | Fail → Reflect (why?) → Fix → Verify |
|
|
10
|
+
| 6. Integrate Fully | Local correct ≠ Global correct; verify all paths |
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
## USBV Workflow
|
|
2
|
+
|
|
3
|
+
**U**nderstand → **S**pecify → **B**uild → **V**alidate
|
|
4
|
+
|
|
5
|
+
| Phase | Purpose | Activities |
|
|
6
|
+
|-------|---------|------------|
|
|
7
|
+
| UNDERSTAND | Know what and why | Intent, Inspect existing code, Constraints |
|
|
8
|
+
| SPECIFY | Define boundaries | Preconditions, Postconditions, Examples |
|
|
9
|
+
| BUILD | Write code | Implement leaves, Compose |
|
|
10
|
+
| VALIDATE | Confirm correctness | Run verification, Review if needed |
|
|
11
|
+
|
|
12
|
+
**Key:** Inspect before Contract. Contracts before Code. Depth varies naturally.
|
|
13
|
+
|
|
14
|
+
**Review Gate:** When verification triggers `review_suggested` (escape hatches ≥3, security paths, low coverage), invoke `/review` before completion.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
## Visible Workflow
|
|
2
|
+
|
|
3
|
+
For complex tasks (3+ functions), show 3 checkpoints in TodoList:
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
□ [UNDERSTAND] Task description, codebase context, constraints
|
|
7
|
+
□ [SPECIFY] Contracts and design decomposition
|
|
8
|
+
□ [VALIDATE] Verification results, Review Gate if triggered, integration status
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**BUILD is internal work** — not shown in TodoList.
|
|
12
|
+
|
|
13
|
+
**Show contracts before code.** Example:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
[SPECIFY] calculate_discount:
|
|
17
|
+
PRECONDITION: price > 0 AND 0 <= rate <= 1
|
|
18
|
+
POSTCONDITION: result >= 0
|
|
19
|
+
FUNCTION calculate_discount(price, rate): ...
|
|
20
|
+
|
|
21
|
+
[BUILD] Now coding...
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**When to use:** New features (3+ functions), architectural changes, Core modifications.
|
|
25
|
+
**Skip for:** Single-line fixes, documentation, trivial refactoring.
|