Implication System
The implication system is EnsuraScript's secret weapon - it automatically infers prerequisite guarantees so you don't have to write them.
The Problem
In traditional automation, you must think about every step:
# Must remember ALL the steps
touch secrets.db # 1. Create file
chmod +rw secrets.db # 2. Make it readable/writable
encrypt secrets.db # 3. Encrypt it
chmod 0600 secrets.db # 4. Lock down permissionsMiss a step? Your automation breaks.
The EnsuraScript Solution
Just declare the end state:
ensure encrypted on file "secrets.db" with AES:256 key "env:KEY"EnsuraScript automatically infers:
- File must exist (can't encrypt nothing)
- File must be readable (can't read to encrypt)
- File must be writable (can't write encrypted data)
How It Works
Implication Rules
The language has built-in implication rules:
encrypted → exists, readable, writable
readable → exists
writable → exists
permissions → exists
checksum → exists, readable
content → existsThe → means "implies". So encrypted → exists reads as "encrypted implies exists".
Expansion Process
When you write:
ensure encrypted on file "secrets.db"The imply phase expands it to:
1. ensure exists on file "secrets.db" [IMPLIED]
2. ensure readable on file "secrets.db" [IMPLIED]
3. ensure writable on file "secrets.db" [IMPLIED]
4. ensure encrypted on file "secrets.db" [ORIGINAL]View this with:
ensura explain config.ensOutput:
Guarantees (4 total, 3 implied):
1. [IMPLIED] [fs.native] ensure exists on file "secrets.db"
2. [IMPLIED] [fs.native] ensure readable on file "secrets.db"
3. [IMPLIED] [fs.native] ensure writable on file "secrets.db"
4. [AES:256] ensure encrypted on file "secrets.db" with AES:256 key "env:KEY"Complete Implication Rules
From the codebase analysis:
| Condition | Implies |
|---|---|
encrypted | exists, readable, writable |
readable | exists |
writable | exists |
permissions | exists |
checksum | exists, readable |
content | exists |
listening | running |
healthy | running |
status_code | reachable |
tls | reachable |
backed_up | exists |
Transitive Implications
Implications are transitive:
ensure checksumExpands to:
1. ensure exists [IMPLIED from readable]
2. ensure readable [IMPLIED from checksum]
3. ensure checksum [ORIGINAL]Because checksum → readable and readable → exists.
Conflict Detection
The implication system detects conflicting conditions:
ensure encrypted on file "data.txt"
ensure unencrypted on file "data.txt" # CONFLICT!Compilation error:
Error: Conflicting conditions on file "data.txt": encrypted vs unencryptedOther conflicts:
runningvsstoppedencryptedvsunencrypted
Deduplication
If you manually write implied guarantees, they're deduplicated:
ensure exists on file "data.txt"
ensure readable on file "data.txt"
ensure encrypted on file "data.txt"After implication expansion (with deduplication):
1. ensure exists [MANUAL + IMPLIED]
2. ensure readable [MANUAL + IMPLIED]
3. ensure writable [IMPLIED from encrypted]
4. ensure encrypted [ORIGINAL]No duplicates - exists and readable aren't added twice.
Execution Order
Implied guarantees are inserted before the original in the dependency graph:
ensure encrypted on file "secrets.db"
ensure backed_up on file "secrets.db"Execution order:
1. exists (implied by encrypted)
2. readable (implied by encrypted)
3. writable (implied by encrypted)
4. encrypted (original)
5. backed_up (original)This ensures prerequisites are satisfied before the main guarantee.
Why This Matters
Less Code
Without implications:
ensure exists on file "secrets.db"
ensure readable on file "secrets.db"
ensure writable on file "secrets.db"
ensure encrypted on file "secrets.db"
ensure permissions on file "secrets.db" with posix mode "0600"With implications:
ensure encrypted on file "secrets.db"
ensure permissions on file "secrets.db" with posix mode "0600"Fewer Bugs
You can't forget a prerequisite - the language infers it.
Better Intent Expression
ensure encrypted says what you want, not how to achieve it. The language figures out the "how".
Implementation Details
The implication phase happens in the compilation pipeline:
Source → Lexer → Parser → Binder → Imply → Graph → Planner → Runtime
↑
We are hereSee the code in pkg/imply/imply.go.
The process:
- Traverse the bound AST
- For each
ensurestatement, look up implications - Generate new
ensurestatements for implied conditions - Insert them before the original in the AST
- Continue to dependency graph building
Next Steps
Continue to Execution Model to understand topological sorting and continuous enforcement.