tldr-cli 0.1.3

CLI binary for TLDR code analysis tool
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
# Contracts & Flow Commands - Rust Port Specification

**Created:** 2026-02-03
**Author:** architect-agent
**Source:** Python v1 at `/Users/cosimo/.opc-dev/opc/packages/tldr-code/tldr/`
**Target:** `/Users/cosimo/.opc-dev/opc/packages/tldr-code/tldr-rs/crates/tldr-cli/src/commands/contracts/`

## Overview

This specification defines the Rust port of 7 TLDR commands for contract inference, behavioral specification extraction, and program flow analysis. These commands help users understand function contracts, invariants, and data flow paths through their code.

The port follows established patterns from the existing Rust CLI (e.g., `slice.rs`, `daemon/` module organization).

## Module Architecture

```
contracts/
├── mod.rs              # Module exports and re-exports
├── error.rs            # ContractsError enum and Result type
├── types.rs            # Shared data types across all commands
├── contracts.rs        # contracts command - pre/postcondition inference
├── invariants.rs       # invariants command - Daikon-lite inference
├── specs.rs            # specs command - test-derived specifications
├── verify.rs           # verify command - aggregated verification dashboard
├── dead_stores.rs      # dead-stores command - SSA-based dead store detection
├── bounds.rs           # bounds command - interval analysis
└── chop.rs             # chop command - program slice intersection
```

## Shared Types (`types.rs`)

### Confidence Level

```rust
use serde::{Deserialize, Serialize};

/// Confidence level for inferred contracts
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Confidence {
    /// Direct code evidence (guard clause, assertion)
    High,
    /// Inferred from patterns or types
    Medium,
    /// Derived from type hints only
    Low,
}

impl Default for Confidence {
    fn default() -> Self {
        Self::Medium
    }
}
```

### Condition (Contract Element)

```rust
/// A single contract condition (precondition, postcondition, or invariant)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Condition {
    /// Variable name this condition applies to
    pub variable: String,
    
    /// Human-readable constraint expression (e.g., "x > 0", "isinstance(x, str)")
    pub constraint: String,
    
    /// Source line where condition was detected
    pub source_line: u32,
    
    /// Confidence level
    pub confidence: Confidence,
}
```

### Invariant Kind

```rust
/// Types of invariants that can be inferred
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum InvariantKind {
    /// Type invariant (e.g., "x: int")
    Type,
    /// Non-null invariant
    NonNull,
    /// Non-negative numeric
    NonNegative,
    /// Positive numeric
    Positive,
    /// Range constraint
    Range,
    /// Ordering relation between parameters
    Relation,
    /// Length constraint
    Length,
}
```

### Spec Types

```rust
/// Input/Output specification from test assertion
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InputOutputSpec {
    pub function: String,
    pub inputs: Vec<serde_json::Value>,
    pub output: serde_json::Value,
    pub test_function: String,
    pub line: u32,
    pub confidence: Confidence,
}

/// Exception specification from pytest.raises
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExceptionSpec {
    pub function: String,
    pub inputs: Vec<serde_json::Value>,
    pub exception_type: String,
    pub match_pattern: Option<String>,
    pub test_function: String,
    pub line: u32,
    pub confidence: Confidence,
}

/// Property specification (type, length, bounds)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PropertySpec {
    pub function: String,
    /// "type", "length", "bounds", "boolean", "membership", "not_none"
    pub property_type: String,
    pub constraint: String,
    pub test_function: String,
    pub line: u32,
    pub confidence: Confidence,
}
```

### Interval

```rust
/// Numeric interval [lo, hi] for bounds analysis
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Interval {
    /// Lower bound (f64::NEG_INFINITY for unbounded)
    pub lo: f64,
    /// Upper bound (f64::INFINITY for unbounded)
    pub hi: f64,
}

impl Interval {
    pub fn const_val(n: f64) -> Self {
        Self { lo: n, hi: n }
    }
    
    pub fn top() -> Self {
        Self { lo: f64::NEG_INFINITY, hi: f64::INFINITY }
    }
    
    pub fn bottom() -> Self {
        Self { lo: f64::INFINITY, hi: f64::NEG_INFINITY }
    }
    
    pub fn is_bottom(&self) -> bool {
        self.lo > self.hi
    }
    
    pub fn contains(&self, n: f64) -> bool {
        !self.is_bottom() && self.lo <= n && n <= self.hi
    }
}
```

## Error Types (`error.rs`)

```rust
use std::path::PathBuf;
use thiserror::Error;

/// Errors specific to contracts and flow analysis commands
#[derive(Debug, Error)]
pub enum ContractsError {
    /// Source file not found
    #[error("file not found: {}", path.display())]
    FileNotFound { path: PathBuf },
    
    /// Function not found in source
    #[error("function '{function}' not found in {}", file.display())]
    FunctionNotFound { function: String, file: PathBuf },
    
    /// Test path not found
    #[error("test path not found: {}", path.display())]
    TestPathNotFound { path: PathBuf },
    
    /// Line outside function range
    #[error("line {line} is outside function '{function}' (lines {start}-{end})")]
    LineOutsideFunction {
        line: u32,
        function: String,
        start: u32,
        end: u32,
    },
    
    /// Parse error in source file
    #[error("parse error in {}: {message}", file.display())]
    ParseError { file: PathBuf, message: String },
    
    /// SSA construction failed
    #[error("SSA construction failed: {0}")]
    SsaError(String),
    
    /// Analysis did not converge
    #[error("analysis did not converge after {iterations} iterations")]
    DidNotConverge { iterations: u32 },
    
    /// Sub-analysis failed in verify command
    #[error("sub-analysis '{name}' failed: {message}")]
    SubAnalysisFailed { name: String, message: String },
    
    /// No test directory found
    #[error("no test directory found in {}", project.display())]
    NoTestDirectory { project: PathBuf },
    
    /// Generic IO error
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
    
    /// JSON serialization error
    #[error("JSON error: {0}")]
    Json(#[from] serde_json::Error),
}

/// Result type for contracts commands
pub type ContractsResult<T> = Result<T, ContractsError>;
```

---

## Command Specifications

### 1. contracts

**Purpose:** Infer pre/postconditions from guard clauses, assertions, and isinstance checks.

#### CLI Interface

```
tldr contracts <file> <function> [OPTIONS]

Arguments:
  <file>      Source file to analyze (required)
  <function>  Function name to analyze (required)

Options:
  -f, --format <FORMAT>  Output format [default: json] [possible values: json, text]
  -l, --lang <LANG>      Language override (auto-detected if not specified)
```

#### Args Struct

```rust
#[derive(Debug, Args)]
pub struct ContractsArgs {
    /// Source file to analyze
    pub file: PathBuf,
    
    /// Function name to analyze
    pub function: String,
    
    /// Output format
    #[arg(long, short = 'f', default_value = "json")]
    pub format: OutputFormat,
    
    /// Language override
    #[arg(long, short = 'l')]
    pub lang: Option<Language>,
}
```

#### Output Type

```rust
#[derive(Debug, Serialize)]
pub struct ContractsReport {
    pub function: String,
    pub file: PathBuf,
    pub preconditions: Vec<Condition>,
    pub postconditions: Vec<Condition>,
    pub invariants: Vec<Condition>,
}
```

#### Behavioral Contracts

**Preconditions (requires):**
- `file` must exist and be readable
- `function` must be a valid identifier

**Postconditions (ensures):**
- Returns `ContractsReport` with detected conditions
- Each condition has a valid `source_line` within the function
- Empty vectors are valid (no contracts detected)

**Error Conditions:**
| Condition | Error |
|-----------|-------|
| File does not exist | `ContractsError::FileNotFound` |
| Function not in file | `ContractsError::FunctionNotFound` |
| Parse failure | `ContractsError::ParseError` |

#### Detection Patterns

| Pattern | Confidence | Example |
|---------|------------|---------|
| `if <cond>: raise` | High | `if x < 0: raise ValueError` -> precond: `x >= 0` |
| `assert <cond>` | High | `assert isinstance(x, str)` -> precond: `isinstance(x, str)` |
| `if not isinstance(...)`: raise | High | Direct type precondition |
| Assert after `result =` | High | Postcondition on result |
| Type annotations | Low | `def f(x: int)` -> precond: `isinstance(x, int)` |

#### Text Output Format

```
Function: process_data
  Preconditions:
    - x >= 0 (x, line 10, high)
    - isinstance(data, list) (data, line 11, high)
  Postconditions:
    - result is not None (result, line 25, high)
  Invariants:
    (none detected)
```

---

### 2. invariants

**Purpose:** Daikon-lite inference of likely invariants from test execution traces.

#### CLI Interface

```
tldr invariants <file> --from-tests <test_path> [OPTIONS]

Arguments:
  <file>  Source file containing functions to analyze (required)

Options:
  -t, --from-tests <PATH>    Test file or directory (required)
  -f, --format <FORMAT>      Output format [default: json]
  --function <NAME>          Filter to specific function
  --min-obs <N>              Minimum observations to report [default: 1]
```

#### Args Struct

```rust
#[derive(Debug, Args)]
pub struct InvariantsArgs {
    /// Source file to analyze
    pub file: PathBuf,
    
    /// Test file or directory for tracing
    #[arg(long = "from-tests", short = 't')]
    pub from_tests: PathBuf,
    
    /// Output format
    #[arg(long, short = 'f', default_value = "json")]
    pub format: OutputFormat,
    
    /// Filter to specific function
    #[arg(long)]
    pub function: Option<String>,
    
    /// Minimum observations required
    #[arg(long, default_value = "1")]
    pub min_obs: u32,
}
```

#### Output Type

```rust
#[derive(Debug, Serialize)]
pub struct Invariant {
    pub variable: String,
    pub kind: InvariantKind,
    pub expression: String,
    pub confidence: Confidence,
    pub observations: u32,
    pub counterexample_count: u32,
}

#[derive(Debug, Serialize)]
pub struct FunctionInvariants {
    pub function_name: String,
    pub preconditions: Vec<Invariant>,
    pub postconditions: Vec<Invariant>,
    pub observation_count: u32,
}

#[derive(Debug, Serialize)]
pub struct InvariantsReport {
    pub functions: Vec<FunctionInvariants>,
    pub summary: InvariantsSummary,
}

#[derive(Debug, Serialize)]
pub struct InvariantsSummary {
    pub total_observations: u32,
    pub total_invariants: u32,
    pub by_kind: HashMap<String, u32>,
}
```

#### Behavioral Contracts

**Preconditions:**
- `file` must exist and be valid source
- `from_tests` must exist (file or directory)

**Postconditions:**
- Returns invariants for all functions with observations >= `min_obs`
- Confidence is calculated: low (<5 obs), medium (5-9), high (10+)

**Error Conditions:**
| Condition | Error |
|-----------|-------|
| Source file not found | `ContractsError::FileNotFound` |
| Test path not found | `ContractsError::TestPathNotFound` |

#### Invariant Detection Rules

| Kind | Detection Rule | Example |
|------|---------------|---------|
| Type | All values same type | `x: int` |
| NonNull | No None values observed | `x is not None` |
| NonNegative | All numeric values >= 0 | `x >= 0` |
| Positive | All numeric values > 0 | `x > 0` |
| Range | Track min/max observed | `0 <= x <= 100` |
| Relation | p1 < p2 for all observations | `start < end` |

#### Text Output Format

```
Function: calculate (15 observations)
  Requires: x: int [high]
  Requires: x >= 0 [high]
  Ensures: result: float [high]
  Ensures: result >= 0 [medium]
```

---

### 3. specs

**Purpose:** Extract behavioral specifications from pytest test files.

#### CLI Interface

```
tldr specs --from-tests <test_path> [OPTIONS]

Options:
  -t, --from-tests <PATH>    Test file or directory (required)
  -f, --format <FORMAT>      Output format [default: json]
  --function <NAME>          Filter to specific function under test
  --source <PATH>            Source directory for cross-referencing
```

#### Args Struct

```rust
#[derive(Debug, Args)]
pub struct SpecsArgs {
    /// Test file or directory
    #[arg(long = "from-tests", short = 't')]
    pub from_tests: PathBuf,
    
    /// Output format
    #[arg(long, short = 'f', default_value = "json")]
    pub format: OutputFormat,
    
    /// Filter to specific function
    #[arg(long)]
    pub function: Option<String>,
    
    /// Source directory for cross-referencing
    #[arg(long)]
    pub source: Option<PathBuf>,
}
```

#### Output Type

```rust
#[derive(Debug, Serialize)]
pub struct FunctionSpecs {
    pub function_name: String,
    pub summary: String,  // e.g., "3 input/output, 1 raises"
    pub test_count: u32,
    pub input_output_specs: Vec<InputOutputSpec>,
    pub exception_specs: Vec<ExceptionSpec>,
    pub property_specs: Vec<PropertySpec>,
}

#[derive(Debug, Serialize)]
pub struct SpecsReport {
    pub functions: Vec<FunctionSpecs>,
    pub summary: SpecsSummary,
}

#[derive(Debug, Serialize)]
pub struct SpecsSummary {
    pub total_specs: u32,
    pub by_type: SpecsByType,
    pub test_functions_scanned: u32,
    pub test_files_scanned: u32,
    pub functions_found: u32,
}

#[derive(Debug, Serialize)]
pub struct SpecsByType {
    pub input_output: u32,
    pub exception: u32,
    pub property: u32,
}
```

#### Behavioral Contracts

**Preconditions:**
- `from_tests` path must exist

**Postconditions:**
- Scans all `test_*.py` files recursively
- Extracts specs from `assert func(args) == expected`
- Extracts specs from `with pytest.raises(Exception)`
- Extracts property specs from `isinstance`, `len()` assertions

**Error Conditions:**
| Condition | Error |
|-----------|-------|
| Path not found | `ContractsError::TestPathNotFound` |

#### Extraction Patterns

| Pattern | Spec Type | Example |
|---------|-----------|---------|
| `assert f(x) == y` | InputOutput | `add(2, 3) == 5` |
| `with pytest.raises(E)` | Exception | `raises(ValueError)` |
| `assert isinstance(f(x), T)` | Property (type) | `isinstance(result, list)` |
| `assert len(f(x)) == n` | Property (length) | `len(result) == 3` |
| `assert f(x) > n` | Property (bounds) | `result > 0` |
| `assert "key" in f(x)` | Property (membership) | `"id" in result` |

#### Text Output Format

```
Function: add
  IO: add(2, 3) == 5
  IO: add(-1, 1) == 0
  Raises: ValueError

Function: parse
  Property (type): isinstance(result, dict)
  Property (length): len(result) == 2

Total specs: 5
```

---

### 4. verify

**Purpose:** Aggregated verification dashboard combining multiple analyses.

#### CLI Interface

```
tldr verify [path] [OPTIONS]

Arguments:
  [path]  Directory to analyze [default: .]

Options:
  -f, --format <FORMAT>    Output format [default: json]
  -l, --lang <LANG>        Language override
  --detail <ANALYSIS>      Show specific sub-analysis result
  --quick                  Quick mode (skip invariants and patterns)
```

#### Args Struct

```rust
#[derive(Debug, Args)]
pub struct VerifyArgs {
    /// Directory to analyze
    #[arg(default_value = ".")]
    pub path: PathBuf,
    
    /// Output format
    #[arg(long, short = 'f', default_value = "json")]
    pub format: OutputFormat,
    
    /// Language override
    #[arg(long, short = 'l')]
    pub lang: Option<Language>,
    
    /// Show specific sub-analysis
    #[arg(long)]
    pub detail: Option<String>,
    
    /// Quick mode (skip slow analyses)
    #[arg(long)]
    pub quick: bool,
}
```

#### Output Type

```rust
#[derive(Debug, Serialize)]
pub struct SubAnalysisResult {
    pub name: String,
    pub success: bool,
    pub error: Option<String>,
    pub elapsed_ms: u64,
    pub data: Option<serde_json::Value>,
}

#[derive(Debug, Serialize)]
pub struct VerifyReport {
    pub path: PathBuf,
    pub sub_results: HashMap<String, SubAnalysisResult>,
    pub summary: VerifySummary,
    pub total_elapsed_ms: u64,
}

#[derive(Debug, Serialize)]
pub struct VerifySummary {
    pub spec_count: u32,
    pub invariant_count: u32,
    pub contract_count: u32,
    pub annotated_count: u32,
    pub behavioral_count: u32,
    pub pattern_count: u32,
    pub pattern_high_confidence: u32,
    pub coverage: CoverageInfo,
}

#[derive(Debug, Serialize)]
pub struct CoverageInfo {
    pub constrained_functions: u32,
    pub total_functions: u32,
    pub coverage_pct: f64,
}
```

#### Behavioral Contracts

**Sub-analyses run:**
1. `contracts` - Contract inference on all files
2. `behavioral` - Behavioral model extraction
3. `annotated` - Annotated[T] constraint extraction
4. `specs` - Test spec extraction (if test dir found)
5. `invariants` - Daikon-lite (skipped in quick mode)
6. `patterns` - Pattern mining (skipped in quick mode)

**Postconditions:**
- Each sub-analysis reports success/failure independently
- Coverage percentage calculated from constrained vs total functions
- Maximum 500 files analyzed

**Error Conditions:**
- Individual sub-analysis failures are captured, not propagated

#### Text Output Format

```
Verification: ./src
==================================================
Test Specs:    42 behavioral specs extracted
Invariants:    15 inferred invariants
Contracts:     28 pre/postconditions inferred
Annotations:   5 Annotated[T] constraints found
Behaviors:     18 functions with behavioral models
Patterns:      3 project patterns (2 high-confidence)

Constraint Coverage:
  Functions with any constraint: 45/60 (75.0%)

Elapsed: 1234ms
```

---

### 5. dead-stores

**Purpose:** SSA-based dead store detection - find assignments that are never read.

#### CLI Interface

```
tldr dead-stores <file> <function> [OPTIONS]

Arguments:
  <file>      Source file to analyze (required)
  <function>  Function name to analyze (required)

Options:
  -l, --lang <LANG>     Language override
  --compare             Compare with live-vars based detection
```

#### Args Struct

```rust
#[derive(Debug, Args)]
pub struct DeadStoresArgs {
    /// Source file to analyze
    pub file: PathBuf,
    
    /// Function name to analyze
    pub function: String,
    
    /// Language override
    #[arg(long, short = 'l')]
    pub lang: Option<Language>,
    
    /// Compare SSA vs liveness-based detection
    #[arg(long)]
    pub compare: bool,
}
```

#### Output Type

```rust
#[derive(Debug, Serialize)]
pub struct DeadStore {
    /// Variable name that was assigned
    pub variable: String,
    /// SSA version name (e.g., "x_2")
    pub ssa_name: String,
    /// Line number of the dead assignment
    pub line: u32,
    /// Block ID where assignment occurs
    pub block_id: u32,
    /// Whether this is a phi function definition
    pub is_phi: bool,
}

impl DeadStore {
    pub fn to_dict(&self) -> serde_json::Value {
        serde_json::json!({
            "variable": self.variable,
            "ssa_name": self.ssa_name,
            "line": self.line,
            "block_id": self.block_id,
            "is_phi": self.is_phi,
        })
    }
}

#[derive(Debug, Serialize)]
pub struct DeadStoresReport {
    pub function: String,
    pub file: PathBuf,
    pub dead_stores_ssa: Vec<DeadStore>,
    pub count: u32,
    /// Only present if --compare flag used
    pub dead_stores_live_vars: Option<Vec<serde_json::Value>>,
    pub live_vars_count: Option<u32>,
}
```

#### Core Algorithm: `find_dead_stores_ssa`

**THIS FUNCTION MUST BE IMPLEMENTED** (missing in Python v1)

```rust
/// Find dead stores in SSA form.
/// 
/// A definition is dead if it has no uses (empty use_sites).
/// Phi function definitions without uses may be normal in some cases.
/// 
/// # Arguments
/// * `ssa_info` - SSA analysis results with def_sites and use_sites
/// 
/// # Returns
/// List of DeadStore instances for each dead definition
pub fn find_dead_stores_ssa(ssa_info: &SsaInfo) -> Vec<DeadStore> {
    let mut dead_stores = Vec::new();
    
    for (ssa_name, def_site) in &ssa_info.def_sites {
        // Get use sites for this definition
        let uses = ssa_info.use_sites.get(ssa_name);
        
        // If no uses, it's a dead store
        if uses.map_or(true, |u| u.is_empty()) {
            // Extract original variable name (strip version suffix)
            let original_var = ssa_name
                .rsplit('_')
                .skip(1)
                .collect::<Vec<_>>()
                .into_iter()
                .rev()
                .collect::<Vec<_>>()
                .join("_");
            
            let is_phi = def_site.get("is_phi")
                .and_then(|v| v.as_bool())
                .unwrap_or(false);
            
            // Skip phi functions without uses (may be normal at loop exits)
            // User can decide whether to filter these
            
            dead_stores.push(DeadStore {
                variable: if original_var.is_empty() { 
                    ssa_name.clone() 
                } else { 
                    original_var 
                },
                ssa_name: ssa_name.clone(),
                line: def_site.get("line")
                    .and_then(|v| v.as_u64())
                    .unwrap_or(0) as u32,
                block_id: def_site.get("block")
                    .and_then(|v| v.as_u64())
                    .unwrap_or(0) as u32,
                is_phi,
            });
        }
    }
    
    dead_stores
}
```

#### Behavioral Contracts

**Preconditions:**
- File must exist and be parseable
- Function must exist in file
- SSA construction must succeed

**Postconditions:**
- Returns all definitions with empty use_sites
- Phi functions are flagged separately

**Error Conditions:**
| Condition | Error |
|-----------|-------|
| File not found | `ContractsError::FileNotFound` |
| Function not found | `ContractsError::FunctionNotFound` |
| SSA construction failed | `ContractsError::SsaError` |

---

### 6. bounds

**Purpose:** Interval analysis tracking numeric value ranges through code.

#### CLI Interface

```
tldr bounds <file> [function] [OPTIONS]

Arguments:
  <file>      Source file to analyze (required)
  [function]  Function name (analyzes all if not specified)

Options:
  -f, --format <FORMAT>    Output format [default: json]
  --max-iter <N>           Maximum fixpoint iterations [default: 50]
```

#### Args Struct

```rust
#[derive(Debug, Args)]
pub struct BoundsArgs {
    /// Source file to analyze
    pub file: PathBuf,
    
    /// Function name (optional)
    pub function: Option<String>,
    
    /// Output format
    #[arg(long, short = 'f', default_value = "json")]
    pub format: OutputFormat,
    
    /// Maximum iterations for fixpoint
    #[arg(long, default_value = "50")]
    pub max_iter: u32,
}
```

#### Output Type

```rust
#[derive(Debug, Serialize)]
pub struct IntervalWarning {
    pub line: u32,
    pub kind: String,  // "division_by_zero", "out_of_bounds"
    pub variable: String,
    pub bounds: Interval,
    pub message: String,
}

#[derive(Debug, Serialize)]
pub struct BoundsResult {
    pub function: String,
    pub bounds: HashMap<u32, HashMap<String, Interval>>,  // line -> var -> interval
    pub warnings: Vec<IntervalWarning>,
    pub converged: bool,
    pub iterations: u32,
}
```

#### Behavioral Contracts

**Preconditions:**
- File must exist

**Postconditions:**
- Returns interval bounds for each variable at each line
- Detects potential division by zero (divisor interval contains 0)
- Converges within max_iter or sets `converged = false`

**Interval Lattice Operations:**
- Join: `[a,b] | [c,d] = [min(a,c), max(b,d)]`
- Meet: `[a,b] & [c,d] = [max(a,c), min(b,d)]` or bottom
- Widen: `[a,b] W [c,d] = [c<a ? -inf : a, d>b ? +inf : b]`

**Error Conditions:**
| Condition | Error |
|-----------|-------|
| File not found | `ContractsError::FileNotFound` |
| Did not converge | `ContractsError::DidNotConverge` (warning, still returns result) |

#### Text Output Format

```
Function: calculate
  Converged: true (12 iterations)
  Line 10: x in [0, 100]
  Line 11: y in [1, 50]
  Line 15: result in [0, inf]
  WARNING [division_by_zero] line 20: Divisor may be zero: x / y in [0, 50]
```

---

### 7. chop

**Purpose:** Compute the intersection of forward and backward slices.

#### CLI Interface

```
tldr chop <file> <function> <source_line> <target_line> [OPTIONS]

Arguments:
  <file>         Source file to analyze (required)
  <function>     Function containing both lines (required)
  <source_line>  Line to trace FROM (required)
  <target_line>  Line to trace TO (required)

Options:
  -l, --lang <LANG>  Language override
```

#### Args Struct

```rust
#[derive(Debug, Args)]
pub struct ChopArgs {
    /// Source file to analyze
    pub file: PathBuf,
    
    /// Function name
    pub function: String,
    
    /// Line to trace from
    pub source_line: u32,
    
    /// Line to trace to
    pub target_line: u32,
    
    /// Language override
    #[arg(long, short = 'l')]
    pub lang: Option<Language>,
}
```

#### Output Type

```rust
#[derive(Debug, Serialize)]
pub struct ChopResult {
    /// Lines on the dependency path (sorted)
    pub lines: Vec<u32>,
    pub count: u32,
    pub source_line: u32,
    pub target_line: u32,
    /// True if source_line is in backward_slice(target_line)
    pub path_exists: bool,
    pub function: String,
    /// Human-readable explanation
    pub explanation: Option<String>,
}
```

#### Core Algorithm

```rust
/// Compute chop slice: forward_slice(source) ∩ backward_slice(target)
/// 
/// # Algorithm
/// 1. Compute forward_slice(source_line) - statements source can affect
/// 2. Compute backward_slice(target_line) - statements that can affect target
/// 3. path_exists = source_line ∈ backward_slice(target_line)
/// 4. chop = forward ∩ backward
pub fn chop(
    source_or_path: &str,
    function_name: &str,
    source_line: u32,
    target_line: u32,
    language: Language,
) -> ContractsResult<ChopResult> {
    // Same line case
    if source_line == target_line {
        return Ok(ChopResult {
            lines: vec![source_line],
            count: 1,
            source_line,
            target_line,
            path_exists: true,
            function: function_name.to_string(),
            explanation: Some(format!(
                "Source and target are the same line ({}).", 
                source_line
            )),
        });
    }
    
    // Extract PDG
    let pdg = extract_pdg(source_or_path, function_name, language)?;
    
    // Validate lines are in function range
    let (func_start, func_end) = pdg.line_range();
    if source_line < func_start || source_line > func_end {
        return Err(ContractsError::LineOutsideFunction {
            line: source_line,
            function: function_name.to_string(),
            start: func_start,
            end: func_end,
        });
    }
    if target_line < func_start || target_line > func_end {
        return Err(ContractsError::LineOutsideFunction {
            line: target_line,
            function: function_name.to_string(),
            start: func_start,
            end: func_end,
        });
    }
    
    // Compute slices
    let forward = pdg.forward_slice(source_line);
    let backward = pdg.backward_slice(target_line);
    
    // Check path existence
    let path_exists = backward.contains(&source_line);
    
    if !path_exists {
        return Ok(ChopResult {
            lines: vec![],
            count: 0,
            source_line,
            target_line,
            path_exists: false,
            function: function_name.to_string(),
            explanation: Some(format!(
                "No dependency path from line {} to line {}. \
                 The source line does not affect the target line.",
                source_line, target_line
            )),
        });
    }
    
    // Compute intersection
    let chop_lines: HashSet<u32> = forward.intersection(&backward).copied().collect();
    let mut lines: Vec<u32> = chop_lines.into_iter().collect();
    lines.sort();
    
    Ok(ChopResult {
        count: lines.len() as u32,
        lines,
        source_line,
        target_line,
        path_exists: true,
        function: function_name.to_string(),
        explanation: Some(format!(
            "Found {} lines on the dependency path from line {} to line {}.",
            lines.len(), source_line, target_line
        )),
    })
}
```

#### Behavioral Contracts

**Preconditions:**
- File must exist
- Function must exist in file
- Both lines must be within function range

**Postconditions:**
- If `path_exists` is false, `lines` is empty
- If `path_exists` is true, both `source_line` and `target_line` are in `lines`
- `lines` is always sorted

**Invariants:**
- `lines` is subset of `forward_slice(source_line)`
- `lines` is subset of `backward_slice(target_line)`

**Error Conditions:**
| Condition | Error |
|-----------|-------|
| File not found | `ContractsError::FileNotFound` |
| Function not found | `ContractsError::FunctionNotFound` |
| Line outside function | `ContractsError::LineOutsideFunction` |

---

## Integration Points

### Dependencies on Other Crates

```toml
# tldr-cli/Cargo.toml additions
[dependencies]
tldr-core = { path = "../tldr-core" }
```

### Required from tldr-core

These functions/types must be available or ported:

```rust
// From tldr-core
pub use tldr_core::{
    // PDG extraction
    extract_pdg, PdgInfo,
    // CFG extraction  
    extract_cfg, CfgInfo,
    // SSA construction
    compute_ssa, SsaInfo, PhiFunction,
    // Slice operations
    forward_slice, backward_slice,
    // Language detection
    Language,
};
```

### Module Registration

Add to `commands/mod.rs`:

```rust
// Contracts & Flow commands (Session 18)
pub mod contracts;

// Re-export Args types
pub use contracts::{
    ContractsArgs, InvariantsArgs, SpecsArgs, VerifyArgs,
    DeadStoresArgs, BoundsArgs, ChopArgs,
};
```

---

## Implementation Phases

### Phase 1: Foundation (types.rs, error.rs, mod.rs)
- Define all shared types
- Define error enum
- Set up module structure

**Acceptance Criteria:**
- [ ] Types compile without errors
- [ ] Error enum covers all failure modes
- [ ] Module re-exports work

### Phase 2: Core Commands (contracts.rs, specs.rs, chop.rs)
- Implement contracts command
- Implement specs command  
- Implement chop command

**Acceptance Criteria:**
- [ ] `tldr contracts` works on Python files
- [ ] `tldr specs` extracts from pytest files
- [ ] `tldr chop` computes slice intersection

### Phase 3: Analysis Commands (bounds.rs, dead_stores.rs)
- Implement bounds (interval analysis)
- Implement dead-stores with `find_dead_stores_ssa`

**Acceptance Criteria:**
- [ ] `tldr bounds` tracks intervals through assignments
- [ ] `tldr dead-stores` finds unused assignments
- [ ] `find_dead_stores_ssa` correctly identifies dead defs

### Phase 4: Integration (verify.rs, invariants.rs)
- Implement verify dashboard
- Implement invariants (requires test execution integration)

**Acceptance Criteria:**
- [ ] `tldr verify` aggregates all sub-analyses
- [ ] Coverage percentage calculated correctly
- [ ] `tldr invariants` works with test tracing

### Phase 5: Testing & Documentation
- Unit tests for each command
- Integration tests with sample files
- Documentation updates

**Coverage Target:** 80%

---

## Risks & Mitigations

| Risk | Impact | Mitigation |
|------|--------|------------|
| `find_dead_stores_ssa` not implemented in Python | High | Implement from scratch using algorithm in spec |
| Test tracing for invariants complex | Medium | Start with static analysis, add tracing later |
| Interval analysis may not converge | Medium | Use widening after N iterations |
| Large files slow verify command | Medium | Maintain 500-file limit, add progress |

---

## Open Questions

- [ ] Should invariants command support languages other than Python?
- [ ] Should chop support thin slicing mode?
- [ ] What level of Annotated[T] support for verify?

---

## Success Criteria

1. All 7 commands pass `--help` and produce valid JSON output
2. `tldr contracts` detects guard clause patterns
3. `tldr specs` extracts specs from pytest assertions
4. `tldr chop` correctly identifies dependency paths
5. `tldr dead-stores` finds assignments with no uses
6. `tldr bounds` warns on potential division by zero
7. `tldr verify` produces coverage percentage
8. Error messages are actionable and specific