@voodocs/cli 0.1.1 → 0.2.0

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,101 @@
1
+ ## [0.2.0] - 2025-12-19
2
+
3
+ ### Added
4
+
5
+ #### Context System - Phase 4: Verification & Visualization
6
+
7
+ **Invariant Checking (`voodocs context check`)**
8
+ - Automatically validate code against documented invariants
9
+ - Pattern-based detection for 6 common security issues (SQL injection, unhashed passwords, logged secrets, etc.)
10
+ - File and line number reporting for violations
11
+ - JSON output format for CI/CD integration
12
+ - Module and invariant filtering options
13
+ - Severity levels (error, warning, info)
14
+
15
+ **Architecture Diagram Generation (`voodocs context diagram`)**
16
+ - Auto-generate visual diagrams from context files
17
+ - 3 diagram types: modules, dependencies, flow
18
+ - Supports Mermaid and D2 formats
19
+ - Auto-renders to PNG using manus-render-diagram
20
+ - Console output or file export
21
+
22
+ **Enhanced Validation (`voodocs context validate`)**
23
+ - Smart suggestions for missing sections
24
+ - Integrated invariant checking with `--check-invariants` flag
25
+ - Completeness, quality, and consistency checks
26
+ - Actionable recommendations for improvement
27
+
28
+ #### Context System - Phase 3: Auto-Generation & Query
29
+
30
+ **Auto-Generation (`voodocs context generate`)**
31
+ - Automatically extract context from @voodocs annotations
32
+ - Recursively scans codebase for annotations
33
+ - Extracts global invariants, assumptions, and modules
34
+ - Supports multiple languages (Python, TypeScript, JavaScript, Java, C++, Go, Rust)
35
+
36
+ **Query System (`voodocs context query`)**
37
+ - Query context like a database using regex patterns
38
+ - Section filtering and multiple output formats (text, JSON, YAML)
39
+
40
+ **Git Integration**
41
+ - Auto-detects Git commit hash and author
42
+ - Integrated into `update` and `sync` commands
43
+
44
+ #### Context System - Phase 2: Versioning & History
45
+
46
+ **Version Management**
47
+ - `voodocs context update`: Update context and increment version
48
+ - `voodocs context sync`: Sync context with code version
49
+ - `voodocs context history`: Show version history
50
+ - `voodocs context diff`: Compare context versions
51
+
52
+ #### Context System - Phase 1: Foundation
53
+
54
+ **Core Commands**
55
+ - `voodocs context init`: Initialize .voodocs.context file
56
+ - `voodocs context view`: View context as Markdown
57
+ - `voodocs context status`: Show context file status
58
+
59
+ ### Changed
60
+ - Enhanced `voodocs context validate` with smart suggestions
61
+ - Improved error messages and user feedback
62
+
63
+ ### Documentation
64
+ - Added comprehensive Phase 3 and Phase 4 implementation guides
65
+ - Updated README with new context system commands
66
+ - Added example architecture diagram
67
+
68
+ ### Performance
69
+ - Invariant checking: < 1 second for 15 files
70
+ - Diagram generation: < 100ms + 1-2s for PNG rendering
71
+ - Enhanced validation: < 50ms
72
+
73
+ ### Code Statistics
74
+ - Total Context System: 3,060 lines across 4 phases
75
+ - 12 context commands implemented
76
+ - 980 lines added in Phase 4
77
+
78
+ ## [0.1.2] - 2025-12-19
79
+
80
+ ### Fixed
81
+ - Fixed bug in documentation generator where `class_invariants` was incorrectly referenced as `invariants` on line 570
82
+ - Function-level invariants now render correctly in generated documentation
83
+
84
+ ### Added
85
+ - Comprehensive example file demonstrating function-level invariants (`examples/test_function_invariants.py`)
86
+ - Examples showing `transfer_funds()`, `binary_search()`, and `BankAccount` class with invariants
87
+ - Documentation in USAGE.md clarifying when to use `invariants` vs `class_invariants`
88
+ - Real-world examples of conservation laws and state constraints using invariants
89
+
90
+ ### Changed
91
+ - Updated USAGE.md annotation fields table to clarify `invariants` usage
92
+ - Added "Function with Invariants" and "Class with Invariants" examples to USAGE.md
93
+
94
+ ### Documentation
95
+ - Clarified that `invariants` can be used at function level for execution-time properties
96
+ - Clarified that `class_invariants` should be used for object state constraints
97
+ - Added examples showing both use cases
98
+
1
99
  ## [0.1.1] - 2025-12-19
2
100
 
3
101
  ### Fixed
package/README.md CHANGED
@@ -84,11 +84,20 @@ voodocs generate ./src --tests-only
84
84
 
85
85
  ## Core Commands
86
86
 
87
- - `voodocs init`: Configure your API key and activate your license
87
+ ### Documentation & Generation
88
+ - `voodocs init`: Initialize VooDocs in your project
88
89
  - `voodocs instruct`: Generate instructions for your AI assistant
89
90
  - `voodocs generate <path>`: Generate docs, tests, and API specs from annotations
90
- - `voodocs status`: Check your current license status and usage
91
- - `voodocs upgrade`: Get information on upgrading your plan
91
+ - `voodocs status`: Check project status and statistics
92
+
93
+ ### Context System (NEW in v0.2.0)
94
+ - `voodocs context init`: Initialize project context file
95
+ - `voodocs context generate`: Auto-generate context from code annotations
96
+ - `voodocs context check`: Validate code against documented invariants
97
+ - `voodocs context diagram`: Generate architecture diagrams
98
+ - `voodocs context validate`: Validate context file with smart suggestions
99
+ - `voodocs context query`: Query context like a database
100
+ - `voodocs context view`: View context as Markdown
92
101
 
93
102
  ## The DarkArts Language
94
103
 
@@ -101,6 +110,29 @@ Voodocs uses the **DarkArts language** - a mathematical and logical notation sys
101
110
 
102
111
  Voodocs then translates this precise, machine-readable format into natural language documentation that humans can easily understand.
103
112
 
113
+ ## What's New in v0.2.0
114
+
115
+ ### 🔍 Invariant Checking
116
+ Automatically validate that your code respects documented invariants:
117
+ ```bash
118
+ voodocs context check
119
+ ```
120
+ Detects security issues like SQL injection, unhashed passwords, and logged secrets.
121
+
122
+ ### 📊 Architecture Diagrams
123
+ Auto-generate visual diagrams from your context:
124
+ ```bash
125
+ voodocs context diagram --type modules --output arch.png --png
126
+ ```
127
+ Supports module diagrams, dependency diagrams, and flow diagrams.
128
+
129
+ ### ✅ Enhanced Validation
130
+ Smart validation with actionable suggestions:
131
+ ```bash
132
+ voodocs context validate --check-invariants
133
+ ```
134
+ Provides completeness checks, quality suggestions, and consistency validation.
135
+
104
136
  ## Why Voodocs?
105
137
 
106
138
  - **AI-First Workflow**: Designed for modern AI-assisted development. Your AI documents as it codes.
@@ -108,6 +140,8 @@ Voodocs then translates this precise, machine-readable format into natural langu
108
140
  - **Massive Time Savings**: Eliminate manual documentation work entirely.
109
141
  - **Higher Code Quality**: Formal specifications lead to more robust and reliable code.
110
142
  - **Precision**: Mathematical notation is unambiguous - no more vague documentation.
143
+ - **Verification**: Automatically check that code respects documented invariants (v0.2.0)
144
+ - **Visualization**: Generate architecture diagrams from context (v0.2.0)
111
145
 
112
146
  ## Example Annotation
113
147
 
package/USAGE.md CHANGED
@@ -247,7 +247,7 @@ function myFunction(x: number): number {
247
247
  | `assumptions` | Assumptions made by the module. |
248
248
  | `preconditions` | Conditions that must be true before a function is called. |
249
249
  | `postconditions` | Conditions that must be true after a function returns. |
250
- | `invariants` | Conditions that must always be true for a class. |
250
+ | `invariants` | Properties that remain unchanged throughout execution. For functions: conditions that hold during execution. For classes: use `class_invariants` for object state constraints. |
251
251
  | `complexity` | Time and space complexity of an algorithm. |
252
252
  | `error_cases` | How the function behaves in error scenarios. |
253
253
  | `security_model` | Security implications and threat model. |
@@ -268,7 +268,57 @@ The DarkArts language is a formal notation used in annotations. It includes:
268
268
 
269
269
  ## Examples
270
270
 
271
- See the `examples/` directory for complete examples in Python and TypeScript.
271
+ ### Function with Invariants
272
+
273
+ **Python**:
274
+ ```python
275
+ def transfer_funds(from_account, to_account, amount):
276
+ """@voodocs
277
+ preconditions: [
278
+ "from_account.balance >= amount",
279
+ "amount > 0",
280
+ "from_account != to_account"
281
+ ]
282
+ postconditions: [
283
+ "from_account.balance = from_account.balance₀ - amount",
284
+ "to_account.balance = to_account.balance₀ + amount"
285
+ ]
286
+ invariants: [
287
+ "total_balance = from_account.balance + to_account.balance (conservation)",
288
+ "from_account.balance >= 0",
289
+ "to_account.balance >= 0"
290
+ ]
291
+ complexity: "O(1)"
292
+ """
293
+ from_account.balance -= amount
294
+ to_account.balance += amount
295
+ ```
296
+
297
+ ### Class with Invariants
298
+
299
+ **Python**:
300
+ ```python
301
+ class BankAccount:
302
+ """@voodocs
303
+ class_invariants: [
304
+ "balance >= 0 (no overdrafts)",
305
+ "account_id is unique and immutable"
306
+ ]
307
+ """
308
+
309
+ def deposit(self, amount):
310
+ """@voodocs
311
+ preconditions: ["amount > 0"]
312
+ postconditions: ["self.balance = self.balance₀ + amount"]
313
+ invariants: [
314
+ "self.balance >= 0 (class invariant maintained)",
315
+ "self.account_id unchanged"
316
+ ]
317
+ """
318
+ self.balance += amount
319
+ ```
320
+
321
+ See the `examples/` directory for more complete examples in Python and TypeScript.
272
322
 
273
323
  ---
274
324
 
package/cli.py CHANGED
@@ -137,7 +137,7 @@ Documentation: https://github.com/3vilEnterprises/vooodooo-magic/tree/main/packa
137
137
  parser.add_argument(
138
138
  "--version",
139
139
  action="version",
140
- version="VooDocs 0.1.1"
140
+ version="VooDocs 0.2.0"
141
141
  )
142
142
 
143
143
  subparsers = parser.add_subparsers(dest="command", help="Available commands")
@@ -372,6 +372,197 @@ Documentation: https://github.com/3vilEnterprises/vooodooo-magic/tree/main/packa
372
372
  help="Telemetry action"
373
373
  )
374
374
 
375
+ # context command
376
+ context_parser = subparsers.add_parser(
377
+ "context",
378
+ help="Manage project context for AI assistants"
379
+ )
380
+ context_subparsers = context_parser.add_subparsers(dest="context_action", help="Context actions")
381
+
382
+ # context init
383
+ context_init_parser = context_subparsers.add_parser(
384
+ "init",
385
+ help="Initialize a new .voodocs.context file"
386
+ )
387
+ context_init_parser.add_argument(
388
+ "--force",
389
+ action="store_true",
390
+ help="Overwrite existing context file"
391
+ )
392
+
393
+ # context view
394
+ context_view_parser = context_subparsers.add_parser(
395
+ "view",
396
+ help="View context as human-readable Markdown"
397
+ )
398
+ context_view_parser.add_argument(
399
+ "--output",
400
+ "-o",
401
+ help="Output file path (default: print to console)"
402
+ )
403
+
404
+ # context status
405
+ context_status_parser = context_subparsers.add_parser(
406
+ "status",
407
+ help="Show context file status"
408
+ )
409
+
410
+ # context update
411
+ context_update_parser = context_subparsers.add_parser(
412
+ "update",
413
+ help="Update context and increment version"
414
+ )
415
+ context_update_parser.add_argument(
416
+ "--description",
417
+ "-d",
418
+ help="Description of the changes"
419
+ )
420
+
421
+ # context sync
422
+ context_sync_parser = context_subparsers.add_parser(
423
+ "sync",
424
+ help="Sync context with code version"
425
+ )
426
+ context_sync_parser.add_argument(
427
+ "--code-version",
428
+ "-v",
429
+ help="New code version (e.g., 2.0.0)"
430
+ )
431
+ context_sync_parser.add_argument(
432
+ "--force",
433
+ action="store_true",
434
+ help="Skip confirmation prompts"
435
+ )
436
+
437
+ # context history
438
+ context_history_parser = context_subparsers.add_parser(
439
+ "history",
440
+ help="Show version history"
441
+ )
442
+
443
+ # context diff
444
+ context_diff_parser = context_subparsers.add_parser(
445
+ "diff",
446
+ help="Compare context versions"
447
+ )
448
+ context_diff_parser.add_argument(
449
+ "version1",
450
+ nargs="?",
451
+ help="First version (older)"
452
+ )
453
+ context_diff_parser.add_argument(
454
+ "version2",
455
+ nargs="?",
456
+ help="Second version (newer, default: current)"
457
+ )
458
+
459
+ # context validate
460
+ context_validate_parser = context_subparsers.add_parser(
461
+ "validate",
462
+ help="Validate context file"
463
+ )
464
+ context_validate_parser.add_argument(
465
+ "--check-version",
466
+ action="store_true",
467
+ help="Check if code version matches detected version"
468
+ )
469
+ context_validate_parser.add_argument(
470
+ "--check-invariants",
471
+ action="store_true",
472
+ help="Check if code respects documented invariants"
473
+ )
474
+
475
+ # context generate
476
+ context_generate_parser = context_subparsers.add_parser(
477
+ "generate",
478
+ help="Generate context from code annotations"
479
+ )
480
+ context_generate_parser.add_argument(
481
+ "source_dir",
482
+ nargs="?",
483
+ help="Directory to scan (default: current directory)"
484
+ )
485
+ context_generate_parser.add_argument(
486
+ "--update",
487
+ action="store_true",
488
+ help="Update existing context file"
489
+ )
490
+
491
+ # context query
492
+ context_query_parser = context_subparsers.add_parser(
493
+ "query",
494
+ help="Query context like a database"
495
+ )
496
+ context_query_parser.add_argument(
497
+ "query",
498
+ help="Search query (keyword or regex)"
499
+ )
500
+ context_query_parser.add_argument(
501
+ "--section",
502
+ "-s",
503
+ help="Specific section to search"
504
+ )
505
+ context_query_parser.add_argument(
506
+ "--format",
507
+ "-f",
508
+ choices=["text", "json", "yaml"],
509
+ default="text",
510
+ help="Output format (default: text)"
511
+ )
512
+
513
+ # context check
514
+ context_check_parser = context_subparsers.add_parser(
515
+ "check",
516
+ help="Check that code respects documented invariants"
517
+ )
518
+ context_check_parser.add_argument(
519
+ "--module",
520
+ "-m",
521
+ help="Filter by module name"
522
+ )
523
+ context_check_parser.add_argument(
524
+ "--invariant",
525
+ "-i",
526
+ help="Filter by invariant text"
527
+ )
528
+ context_check_parser.add_argument(
529
+ "--format",
530
+ "-f",
531
+ choices=["text", "json"],
532
+ default="text",
533
+ help="Output format (default: text)"
534
+ )
535
+
536
+ # context diagram
537
+ context_diagram_parser = context_subparsers.add_parser(
538
+ "diagram",
539
+ help="Generate architecture diagrams from context"
540
+ )
541
+ context_diagram_parser.add_argument(
542
+ "--type",
543
+ "-t",
544
+ choices=["modules", "dependencies", "flow", "all"],
545
+ default="modules",
546
+ help="Diagram type (default: modules)"
547
+ )
548
+ context_diagram_parser.add_argument(
549
+ "--format",
550
+ "-f",
551
+ choices=["mermaid", "d2"],
552
+ default="mermaid",
553
+ help="Output format (default: mermaid)"
554
+ )
555
+ context_diagram_parser.add_argument(
556
+ "--output",
557
+ "-o",
558
+ help="Output file path (default: print to console)"
559
+ )
560
+ context_diagram_parser.add_argument(
561
+ "--png",
562
+ action="store_true",
563
+ help="Render to PNG (requires output file)"
564
+ )
565
+
375
566
  args = parser.parse_args()
376
567
 
377
568
  # Execute command
@@ -393,6 +584,8 @@ Documentation: https://github.com/3vilEnterprises/vooodooo-magic/tree/main/packa
393
584
  cmd_export(args)
394
585
  elif args.command == "telemetry":
395
586
  cmd_telemetry(args)
587
+ elif args.command == "context":
588
+ cmd_context(args)
396
589
  else:
397
590
  parser.print_help()
398
591
  sys.exit(1)
@@ -435,7 +628,7 @@ def cmd_init(args):
435
628
  config = {
436
629
  "project_name": project_name,
437
630
  "language": language,
438
- "version": "0.1.1",
631
+ "version": "0.2.0",
439
632
  "output_dir": "./voodocs-output",
440
633
  "test_framework": "pytest",
441
634
  "api_format": "openapi",
@@ -1353,5 +1546,72 @@ def cmd_telemetry(args):
1353
1546
  client.status()
1354
1547
 
1355
1548
 
1549
+ def cmd_context(args):
1550
+ """Manage project context."""
1551
+ from darkarts.context import (
1552
+ cmd_context_init,
1553
+ cmd_context_view,
1554
+ cmd_context_status,
1555
+ cmd_context_update,
1556
+ cmd_context_sync,
1557
+ cmd_context_history,
1558
+ cmd_context_diff,
1559
+ cmd_context_validate,
1560
+ cmd_context_generate,
1561
+ cmd_context_query,
1562
+ cmd_context_check,
1563
+ cmd_context_diagram
1564
+ )
1565
+
1566
+ if args.context_action == "init":
1567
+ force = getattr(args, 'force', False)
1568
+ sys.exit(cmd_context_init(force=force))
1569
+ elif args.context_action == "view":
1570
+ output_file = getattr(args, 'output', None)
1571
+ sys.exit(cmd_context_view(output_file=output_file))
1572
+ elif args.context_action == "status":
1573
+ sys.exit(cmd_context_status())
1574
+ elif args.context_action == "update":
1575
+ description = getattr(args, 'description', None)
1576
+ sys.exit(cmd_context_update(description=description))
1577
+ elif args.context_action == "sync":
1578
+ code_version = getattr(args, 'code_version', None)
1579
+ force = getattr(args, 'force', False)
1580
+ sys.exit(cmd_context_sync(code_version=code_version, force=force))
1581
+ elif args.context_action == "history":
1582
+ sys.exit(cmd_context_history())
1583
+ elif args.context_action == "diff":
1584
+ version1 = getattr(args, 'version1', None)
1585
+ version2 = getattr(args, 'version2', None)
1586
+ sys.exit(cmd_context_diff(version1=version1, version2=version2))
1587
+ elif args.context_action == "validate":
1588
+ check_version = getattr(args, 'check_version', False)
1589
+ check_invariants = getattr(args, 'check_invariants', False)
1590
+ sys.exit(cmd_context_validate(check_version=check_version, check_invariants=check_invariants))
1591
+ elif args.context_action == "generate":
1592
+ source_dir = getattr(args, 'source_dir', None)
1593
+ update_existing = getattr(args, 'update', False)
1594
+ sys.exit(cmd_context_generate(source_dir=source_dir, update_existing=update_existing))
1595
+ elif args.context_action == "query":
1596
+ query = getattr(args, 'query', '')
1597
+ section = getattr(args, 'section', None)
1598
+ format_type = getattr(args, 'format', 'text')
1599
+ sys.exit(cmd_context_query(query=query, section=section, format=format_type))
1600
+ elif args.context_action == "check":
1601
+ module_filter = getattr(args, 'module', None)
1602
+ invariant_filter = getattr(args, 'invariant', None)
1603
+ format_type = getattr(args, 'format', 'text')
1604
+ sys.exit(cmd_context_check(module_filter=module_filter, invariant_filter=invariant_filter, output_format=format_type))
1605
+ elif args.context_action == "diagram":
1606
+ diagram_type = getattr(args, 'type', 'modules')
1607
+ output_format = getattr(args, 'format', 'mermaid')
1608
+ output_file = getattr(args, 'output', None)
1609
+ render_png = getattr(args, 'png', False)
1610
+ sys.exit(cmd_context_diagram(diagram_type=diagram_type, output_format=output_format, output_file=output_file, render_png=render_png))
1611
+ else:
1612
+ print("Usage: voodocs context {init|view|status|update|sync|history|diff|validate|generate|query|check|diagram}")
1613
+ sys.exit(1)
1614
+
1615
+
1356
1616
  if __name__ == "__main__":
1357
1617
  main()
@@ -0,0 +1,134 @@
1
+ """
2
+ Example demonstrating function-level invariants in VooDocs.
3
+
4
+ This file shows how to use invariants at the function level to document
5
+ properties that must hold throughout the function's execution.
6
+ """
7
+
8
+
9
+ def transfer_funds(from_account, to_account, amount):
10
+ """@voodocs
11
+ preconditions: [
12
+ "from_account.balance >= amount",
13
+ "amount > 0",
14
+ "from_account != to_account"
15
+ ]
16
+ postconditions: [
17
+ "from_account.balance = from_account.balance₀ - amount",
18
+ "to_account.balance = to_account.balance₀ + amount",
19
+ "total_balance = from_account.balance + to_account.balance = total_balance₀"
20
+ ]
21
+ invariants: [
22
+ "total_balance = from_account.balance + to_account.balance (conservation of funds)",
23
+ "from_account.balance >= 0 (no negative balances)",
24
+ "to_account.balance >= 0 (no negative balances)",
25
+ "amount remains constant throughout execution"
26
+ ]
27
+ side_effects: [
28
+ "Modifies from_account.balance",
29
+ "Modifies to_account.balance",
30
+ "Logs transaction to audit trail"
31
+ ]
32
+ complexity: "O(1)"
33
+ """
34
+ # Verify preconditions
35
+ if from_account.balance < amount:
36
+ raise ValueError("Insufficient funds")
37
+ if amount <= 0:
38
+ raise ValueError("Amount must be positive")
39
+ if from_account == to_account:
40
+ raise ValueError("Cannot transfer to same account")
41
+
42
+ # Perform transfer (invariants hold throughout)
43
+ from_account.balance -= amount
44
+ to_account.balance += amount
45
+
46
+ # Log transaction
47
+ print(f"Transferred {amount} from {from_account.id} to {to_account.id}")
48
+
49
+
50
+ def binary_search(arr, target):
51
+ """@voodocs
52
+ preconditions: [
53
+ "arr is sorted in ascending order",
54
+ "∀ i, j: 0 ≤ i < j < len(arr) ⇒ arr[i] ≤ arr[j]"
55
+ ]
56
+ postconditions: [
57
+ "result ≥ 0 ⇒ arr[result] = target",
58
+ "result = -1 ⇒ target ∉ arr"
59
+ ]
60
+ invariants: [
61
+ "0 ≤ left ≤ right < len(arr) (search bounds)",
62
+ "target ∈ arr ⇒ target ∈ arr[left:right+1] (target in search range)",
63
+ "arr remains unchanged (no side effects on input)"
64
+ ]
65
+ complexity: "O(log n) where n = len(arr)"
66
+ """
67
+ left, right = 0, len(arr) - 1
68
+
69
+ while left <= right:
70
+ mid = (left + right) // 2
71
+
72
+ if arr[mid] == target:
73
+ return mid
74
+ elif arr[mid] < target:
75
+ left = mid + 1
76
+ else:
77
+ right = mid - 1
78
+
79
+ return -1
80
+
81
+
82
+ class BankAccount:
83
+ """@voodocs
84
+ class_invariants: [
85
+ "balance >= 0 (no overdrafts)",
86
+ "account_id is unique and immutable",
87
+ "len(transaction_history) >= 0"
88
+ ]
89
+ """
90
+
91
+ def __init__(self, account_id, initial_balance=0):
92
+ """@voodocs
93
+ preconditions: [
94
+ "initial_balance >= 0",
95
+ "account_id is not None"
96
+ ]
97
+ postconditions: [
98
+ "self.balance = initial_balance",
99
+ "self.account_id = account_id",
100
+ "len(self.transaction_history) = 0"
101
+ ]
102
+ invariants: [
103
+ "self.balance >= 0 (established at initialization)"
104
+ ]
105
+ """
106
+ self.account_id = account_id
107
+ self.balance = initial_balance
108
+ self.transaction_history = []
109
+
110
+ def deposit(self, amount):
111
+ """@voodocs
112
+ preconditions: [
113
+ "amount > 0"
114
+ ]
115
+ postconditions: [
116
+ "self.balance = self.balance₀ + amount",
117
+ "len(self.transaction_history) = len(self.transaction_history₀) + 1"
118
+ ]
119
+ invariants: [
120
+ "self.balance >= 0 (class invariant maintained)",
121
+ "amount remains constant",
122
+ "self.account_id unchanged"
123
+ ]
124
+ side_effects: [
125
+ "Modifies self.balance",
126
+ "Appends to self.transaction_history"
127
+ ]
128
+ complexity: "O(1)"
129
+ """
130
+ if amount <= 0:
131
+ raise ValueError("Deposit amount must be positive")
132
+
133
+ self.balance += amount
134
+ self.transaction_history.append(("deposit", amount))