@onlooker-community/ecosystem 0.15.1 → 0.16.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.
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env bats
2
+
3
+ setup() {
4
+ source "${BATS_TEST_DIRNAME}/../helpers/setup.bash"
5
+ setup_test_env
6
+
7
+ PLUGIN_ROOT="${REPO_ROOT}/plugins/governor"
8
+ export CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT"
9
+ # shellcheck disable=SC1091
10
+ source "${PLUGIN_ROOT}/scripts/lib/governor-config.sh"
11
+ }
12
+
13
+ @test "governor is disabled by default" {
14
+ governor_config_load ""
15
+ run governor_config_enabled
16
+ [ "$status" -ne 0 ]
17
+ }
18
+
19
+ @test "user-level settings.json can enable governor" {
20
+ mkdir -p "${HOME}/.claude"
21
+ printf '%s\n' '{"governor":{"enabled":true}}' > "${HOME}/.claude/settings.json"
22
+ governor_config_load ""
23
+ run governor_config_enabled
24
+ [ "$status" -eq 0 ]
25
+ }
26
+
27
+ @test "repo-level settings.json overrides user-level" {
28
+ mkdir -p "${HOME}/.claude"
29
+ printf '%s\n' '{"governor":{"enabled":true}}' > "${HOME}/.claude/settings.json"
30
+ local repo="${BATS_TEST_TMPDIR}/repo"
31
+ mkdir -p "${repo}/.claude"
32
+ printf '%s\n' '{"governor":{"enabled":false}}' > "${repo}/.claude/settings.json"
33
+ governor_config_load "$repo"
34
+ run governor_config_enabled
35
+ [ "$status" -ne 0 ]
36
+ }
37
+
38
+ @test "default enforcement is soft" {
39
+ governor_config_load ""
40
+ local v
41
+ v=$(governor_config_enforcement)
42
+ [ "$v" = "soft" ]
43
+ }
44
+
45
+ @test "enforcement can be overridden to hard" {
46
+ mkdir -p "${HOME}/.claude"
47
+ printf '%s\n' '{"governor":{"enforcement":"hard"}}' > "${HOME}/.claude/settings.json"
48
+ governor_config_load ""
49
+ local v
50
+ v=$(governor_config_enforcement)
51
+ [ "$v" = "hard" ]
52
+ }
53
+
54
+ @test "default tokens budget is 100000" {
55
+ governor_config_load ""
56
+ local v
57
+ v=$(governor_config_get '.governor.session.tokens_default')
58
+ [ "$v" = "100000" ]
59
+ }
60
+
61
+ @test "default cost budget is 1.0" {
62
+ governor_config_load ""
63
+ local v
64
+ v=$(governor_config_get '.governor.session.cost_usd_default')
65
+ [ "$v" = "1.0" ]
66
+ }
67
+
68
+ @test "default safety margin is 1.3" {
69
+ governor_config_load ""
70
+ local v
71
+ v=$(governor_config_get '.governor.estimation.safety_margin')
72
+ [ "$v" = "1.3" ]
73
+ }
74
+
75
+ @test "default hard_stop_margin is 1.5" {
76
+ governor_config_load ""
77
+ local v
78
+ v=$(governor_config_get '.governor.estimation.hard_stop_margin')
79
+ [ "$v" = "1.5" ]
80
+ }
81
+
82
+ @test "default estimation method is tier_table" {
83
+ governor_config_load ""
84
+ local v
85
+ v=$(governor_config_get '.governor.estimation.method')
86
+ [ "$v" = "tier_table" ]
87
+ }
88
+
89
+ @test "governor_config_get returns empty for missing key" {
90
+ governor_config_load ""
91
+ local v
92
+ v=$(governor_config_get '.governor.no_such_key')
93
+ [ -z "$v" ]
94
+ }
95
+
96
+ @test "empty repo_root does not load /.claude/settings.json" {
97
+ # Place a settings.json at the absolute root path that an empty repo_root would produce.
98
+ # On a real machine this won't exist, but in CI it might; the guard should skip it.
99
+ # We verify that a file at / does not influence config by confirming the default holds.
100
+ governor_config_load ""
101
+ run governor_config_enabled
102
+ # Default is disabled — if /.claude/settings.json were loaded with {enabled:true}
103
+ # this would fail. We can't plant a file at / in tests, so we assert the default
104
+ # is intact (regression guard rather than direct injection test).
105
+ [ "$status" -ne 0 ]
106
+ }
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env bats
2
+
3
+ setup() {
4
+ source "${BATS_TEST_DIRNAME}/../helpers/setup.bash"
5
+ setup_test_env
6
+
7
+ PLUGIN_ROOT="${REPO_ROOT}/plugins/governor"
8
+ export CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT"
9
+
10
+ # shellcheck disable=SC1091
11
+ source "${PLUGIN_ROOT}/scripts/lib/governor-config.sh"
12
+ # shellcheck disable=SC1091
13
+ source "${PLUGIN_ROOT}/scripts/lib/governor-estimate.sh"
14
+
15
+ governor_config_load ""
16
+ }
17
+
18
+ @test "governor_estimate_method returns tier_table" {
19
+ local m
20
+ m=$(governor_estimate_method)
21
+ [ "$m" = "tier_table" ]
22
+ }
23
+
24
+ @test "empty input returns a nonzero estimate" {
25
+ local t
26
+ t=$(governor_estimate_tokens "")
27
+ [ "$t" -gt 0 ]
28
+ }
29
+
30
+ @test "prose input produces a positive estimate" {
31
+ local input="This is a plain English sentence with no special characters at all."
32
+ local t
33
+ t=$(governor_estimate_tokens "$input" 1.0)
34
+ [ "$t" -gt 0 ]
35
+ }
36
+
37
+ @test "JSON input produces higher token density estimate than prose" {
38
+ local prose="This is a longer plain English paragraph used as baseline comparison text."
39
+ local json='{"key":"value","nested":{"array":[1,2,3,4,5],"flag":true},"extra":"padding"}'
40
+
41
+ local chars_prose=${#prose}
42
+ local chars_json=${#json}
43
+ local toks_prose
44
+ local toks_json
45
+ toks_prose=$(governor_estimate_tokens "$prose" 1.0)
46
+ toks_json=$(governor_estimate_tokens "$json" 1.0)
47
+
48
+ # JSON uses 3 chars/tok vs 4 for prose, so per char JSON should yield more tokens.
49
+ local ratio_prose=$(( toks_prose * 100 / chars_prose ))
50
+ local ratio_json=$(( toks_json * 100 / chars_json ))
51
+ [ "$ratio_json" -ge "$ratio_prose" ]
52
+ }
53
+
54
+ @test "safety margin multiplies the base estimate" {
55
+ local input="hello world this is a test sentence"
56
+ local base
57
+ local with_margin
58
+ base=$(governor_estimate_tokens "$input" 1.0)
59
+ with_margin=$(governor_estimate_tokens "$input" 1.3)
60
+
61
+ # with_margin should be >= base (margin >= 1.0)
62
+ [ "$with_margin" -ge "$base" ]
63
+ }
64
+
65
+ @test "estimate scales with input length" {
66
+ local short="short"
67
+ local long
68
+ long=$(printf 'x%.0s' {1..500})
69
+ local t_short t_long
70
+ t_short=$(governor_estimate_tokens "$short" 1.0)
71
+ t_long=$(governor_estimate_tokens "$long" 1.0)
72
+ [ "$t_long" -gt "$t_short" ]
73
+ }
74
+
75
+ @test "governor_estimate_cost returns a positive float for nonzero tokens" {
76
+ local cost
77
+ cost=$(governor_estimate_cost 10000)
78
+ # Should be > 0
79
+ awk "BEGIN { exit ($cost > 0) ? 0 : 1 }"
80
+ }
81
+
82
+ @test "governor_estimate_cost returns 0-ish for 0 tokens" {
83
+ local cost
84
+ cost=$(governor_estimate_cost 0)
85
+ [ "$cost" = "0.000000" ] || [ "$cost" = "0" ] || [ "$cost" = "0.0" ]
86
+ }
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # Validates that governor.* events pass @onlooker-community/schema validation.
4
+
5
+ setup() {
6
+ source "${BATS_TEST_DIRNAME}/../helpers/setup.bash"
7
+ setup_test_env
8
+
9
+ PLUGIN_ROOT="${REPO_ROOT}/plugins/governor"
10
+ export CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT"
11
+ export ONLOOKER_EVENTS_LOG="${ONLOOKER_DIR}/logs/onlooker-events.jsonl"
12
+ mkdir -p "$(dirname "$ONLOOKER_EVENTS_LOG")"
13
+
14
+ export _ONLOOKER_EVENT_JS="${REPO_ROOT}/scripts/lib/onlooker-event.mjs"
15
+
16
+ # shellcheck disable=SC1091
17
+ source "${PLUGIN_ROOT}/scripts/lib/governor-config.sh"
18
+ # shellcheck disable=SC1091
19
+ source "${PLUGIN_ROOT}/scripts/lib/governor-events.sh"
20
+
21
+ export CLAUDE_SESSION_ID="bats-gov-session-$$"
22
+ }
23
+
24
+ _validate_latest_event() {
25
+ local last
26
+ last=$(tail -n 1 "$ONLOOKER_EVENTS_LOG")
27
+ [ -n "$last" ] || return 1
28
+ printf '%s' "$last" | ONLOOKER_DIR="$ONLOOKER_DIR" \
29
+ node "${REPO_ROOT}/scripts/lib/onlooker-event.mjs" validate >/dev/null
30
+ }
31
+
32
+ SID="bats-session-000"
33
+ AID="bats-agent-000"
34
+
35
+ @test "governor.gate.checked allow validates" {
36
+ local p
37
+ p=$(jq -n \
38
+ --arg sid "$SID" --arg aid "$AID" \
39
+ '{
40
+ session_id: $sid,
41
+ agent_id: $aid,
42
+ agent_type: "Task",
43
+ decision: "allow",
44
+ estimated_tokens: 5000,
45
+ tokens_available: 95000,
46
+ estimation_method: "tier_table",
47
+ safety_margin: 1.3
48
+ }')
49
+ governor_emit_event "governor.gate.checked" "$p"
50
+ run _validate_latest_event
51
+ [ "$status" -eq 0 ]
52
+ }
53
+
54
+ @test "governor.gate.checked block with reason validates" {
55
+ local p
56
+ p=$(jq -n \
57
+ --arg sid "$SID" --arg aid "$AID" \
58
+ '{
59
+ session_id: $sid,
60
+ agent_id: $aid,
61
+ agent_type: "Task",
62
+ decision: "block",
63
+ reason: "budget_exceeded",
64
+ estimated_tokens: 110000,
65
+ tokens_available: 5000,
66
+ estimation_method: "tier_table",
67
+ safety_margin: 1.3
68
+ }')
69
+ governor_emit_event "governor.gate.checked" "$p"
70
+ run _validate_latest_event
71
+ [ "$status" -eq 0 ]
72
+ }
73
+
74
+ @test "governor.call.recorded validates" {
75
+ local p
76
+ p=$(jq -n \
77
+ --arg sid "$SID" --arg aid "$AID" \
78
+ '{
79
+ session_id: $sid,
80
+ agent_id: $aid,
81
+ agent_type: "Task",
82
+ estimated_tokens: 4200,
83
+ cost_usd_estimated: 0.038,
84
+ duration_ms: 3500
85
+ }')
86
+ governor_emit_event "governor.call.recorded" "$p"
87
+ run _validate_latest_event
88
+ [ "$status" -eq 0 ]
89
+ }
90
+
91
+ @test "governor.call.recorded with actuals validates" {
92
+ local p
93
+ p=$(jq -n \
94
+ --arg sid "$SID" --arg aid "$AID" \
95
+ '{
96
+ session_id: $sid,
97
+ agent_id: $aid,
98
+ agent_type: "Task",
99
+ estimated_tokens: 4200,
100
+ actual_tokens: 3900,
101
+ estimation_error_pct: 7.69,
102
+ cost_usd_estimated: 0.038,
103
+ cost_usd_actual: 0.035,
104
+ duration_ms: 3500,
105
+ tokens_returned_to_pool: 0
106
+ }')
107
+ governor_emit_event "governor.call.recorded" "$p"
108
+ run _validate_latest_event
109
+ [ "$status" -eq 0 ]
110
+ }
111
+
112
+ @test "governor.ledger.write_failed validates" {
113
+ local p
114
+ p=$(jq -n \
115
+ --arg sid "$SID" --arg aid "$AID" \
116
+ '{
117
+ session_id: $sid,
118
+ agent_id: $aid,
119
+ error: "write failed after 3 attempts",
120
+ retry_count: 3,
121
+ ledger_poisoned: true,
122
+ unrecorded_tokens: 4200
123
+ }')
124
+ governor_emit_event "governor.ledger.write_failed" "$p"
125
+ run _validate_latest_event
126
+ [ "$status" -eq 0 ]
127
+ }
128
+
129
+ @test "governor.budget.warning validates" {
130
+ local p
131
+ p=$(jq -n \
132
+ --arg sid "$SID" \
133
+ '{
134
+ budget_usd: 1.0,
135
+ spent_usd: 0.72,
136
+ threshold_pct: 70,
137
+ remaining_usd: 0.28,
138
+ session_id: $sid,
139
+ dimension: "cost_usd"
140
+ }')
141
+ governor_emit_event "governor.budget.warning" "$p"
142
+ run _validate_latest_event
143
+ [ "$status" -eq 0 ]
144
+ }
145
+
146
+ @test "governor.budget.exceeded validates" {
147
+ local p
148
+ p=$(jq -n \
149
+ --arg sid "$SID" --arg aid "$AID" \
150
+ '{
151
+ budget_usd: 1.0,
152
+ spent_usd: 1.05,
153
+ blocked_operation: "Task spawn",
154
+ session_id: $sid,
155
+ agent_id: $aid,
156
+ dimension: "tokens",
157
+ estimated_call_cost: 0.08,
158
+ ceiling_type: "session"
159
+ }')
160
+ governor_emit_event "governor.budget.exceeded" "$p"
161
+ run _validate_latest_event
162
+ [ "$status" -eq 0 ]
163
+ }
164
+
165
+ @test "governor.session.complete validates" {
166
+ local p
167
+ p=$(jq -n \
168
+ --arg sid "$SID" \
169
+ '{
170
+ total_cost_usd: 0.42,
171
+ budget_usd: 1.0,
172
+ under_budget: true,
173
+ session_id: $sid,
174
+ total_tokens: 46200,
175
+ total_api_calls: 11,
176
+ duration_ms: 0,
177
+ calls_blocked: 0,
178
+ calls_warned: 2,
179
+ ledger_poisoned: false
180
+ }')
181
+ governor_emit_event "governor.session.complete" "$p"
182
+ run _validate_latest_event
183
+ [ "$status" -eq 0 ]
184
+ }
185
+
186
+ @test "governor.lock.stale_cleared validates" {
187
+ local p
188
+ p=$(jq -n \
189
+ '{
190
+ lock_path: "/tmp/test.lock.d",
191
+ lock_age_seconds: 120.5,
192
+ pid_verified_dead: false
193
+ }')
194
+ governor_emit_event "governor.lock.stale_cleared" "$p"
195
+ run _validate_latest_event
196
+ [ "$status" -eq 0 ]
197
+ }
198
+
199
+ @test "governor.child.allocated validates" {
200
+ local p
201
+ p=$(jq -n \
202
+ --arg sid "$SID" \
203
+ '{
204
+ session_id: $sid,
205
+ parent_agent_id: "parent-001",
206
+ child_agent_id: "child-001",
207
+ child_agent_type: "tribunal-actor",
208
+ tokens_allocated: 20000,
209
+ cost_usd_allocated: 0.18,
210
+ tokens_remaining_after_allocation: 80000,
211
+ conservation_check_passed: true
212
+ }')
213
+ governor_emit_event "governor.child.allocated" "$p"
214
+ run _validate_latest_event
215
+ [ "$status" -eq 0 ]
216
+ }
217
+
218
+ @test "governor.child.returned validates" {
219
+ local p
220
+ p=$(jq -n \
221
+ --arg sid "$SID" \
222
+ '{
223
+ session_id: $sid,
224
+ parent_agent_id: "parent-001",
225
+ child_agent_id: "child-001",
226
+ tokens_allocated: 20000,
227
+ tokens_consumed: 14200,
228
+ tokens_returned: 5800
229
+ }')
230
+ governor_emit_event "governor.child.returned" "$p"
231
+ run _validate_latest_event
232
+ [ "$status" -eq 0 ]
233
+ }
234
+
235
+ @test "governor_emit_event returns nonzero for unknown event type" {
236
+ run governor_emit_event "governor.no_such_event" '{"session_id":"x"}'
237
+ [ "$status" -ne 0 ]
238
+ }
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env bats
2
+
3
+ setup() {
4
+ source "${BATS_TEST_DIRNAME}/../helpers/setup.bash"
5
+ setup_test_env
6
+
7
+ PLUGIN_ROOT="${REPO_ROOT}/plugins/governor"
8
+ export CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT"
9
+ export ONLOOKER_EVENTS_LOG="${ONLOOKER_DIR}/logs/onlooker-events.jsonl"
10
+ mkdir -p "$(dirname "$ONLOOKER_EVENTS_LOG")"
11
+
12
+ export _ONLOOKER_EVENT_JS="${REPO_ROOT}/scripts/lib/onlooker-event.mjs"
13
+ export CLAUDE_SESSION_ID="bats-ledger-$$"
14
+
15
+ # shellcheck disable=SC1091
16
+ source "${REPO_ROOT}/scripts/lib/portable-lock.sh"
17
+ # shellcheck disable=SC1091
18
+ source "${PLUGIN_ROOT}/scripts/lib/governor-config.sh"
19
+ # shellcheck disable=SC1091
20
+ source "${PLUGIN_ROOT}/scripts/lib/governor-events.sh"
21
+ # shellcheck disable=SC1091
22
+ source "${PLUGIN_ROOT}/scripts/lib/governor-ledger.sh"
23
+
24
+ export ONLOOKER_DIR="${TEST_HOME}/.onlooker"
25
+ SID="ledger-test-session-$$"
26
+ }
27
+
28
+ _make_record() {
29
+ local tokens="${1:-1000}"
30
+ local cost="${2:-0.009}"
31
+ jq -n \
32
+ --arg ts "2026-01-01T00:00:00Z" \
33
+ --arg sid "$SID" \
34
+ --arg aid "test-agent" \
35
+ --argjson est "$tokens" \
36
+ --argjson cost "$cost" \
37
+ '{
38
+ ts: $ts,
39
+ session_id: $sid,
40
+ agent_id: $aid,
41
+ agent_type: "Task",
42
+ estimated_tokens: $est,
43
+ cost_usd_estimated: $cost,
44
+ duration_ms: 1000
45
+ }'
46
+ }
47
+
48
+ _make_reservation() {
49
+ local tokens="${1:-1000}"
50
+ local cost="${2:-0.009}"
51
+ jq -n \
52
+ --arg ts "2026-01-01T00:00:00Z" \
53
+ --arg sid "$SID" \
54
+ --arg aid "test-agent" \
55
+ --argjson est "$tokens" \
56
+ --argjson cost "$cost" \
57
+ '{
58
+ ts: $ts,
59
+ session_id: $sid,
60
+ agent_id: $aid,
61
+ agent_type: "Task",
62
+ estimated_tokens: $est,
63
+ cost_usd_estimated: $cost,
64
+ record_type: "reservation"
65
+ }'
66
+ }
67
+
68
+ _make_completion() {
69
+ local neg_tokens="${1:--1000}"
70
+ local actual_tokens="${2:-900}"
71
+ local cost="${3:-0.009}"
72
+ jq -n \
73
+ --arg ts "2026-01-01T00:00:00Z" \
74
+ --arg sid "$SID" \
75
+ --arg aid "test-agent" \
76
+ --argjson est "$neg_tokens" \
77
+ --argjson actual "$actual_tokens" \
78
+ --argjson cost "$cost" \
79
+ '{
80
+ ts: $ts,
81
+ session_id: $sid,
82
+ agent_id: $aid,
83
+ agent_type: "Task",
84
+ estimated_tokens: $est,
85
+ actual_tokens: $actual,
86
+ cost_usd_estimated: $cost,
87
+ duration_ms: 1500
88
+ }'
89
+ }
90
+
91
+ @test "governor_ledger_path returns a .jsonl path under ONLOOKER_DIR" {
92
+ local p
93
+ p=$(governor_ledger_path "test-session")
94
+ [[ "$p" == *".jsonl" ]]
95
+ [[ "$p" == *"test-session"* ]]
96
+ }
97
+
98
+ @test "governor_ledger_total_tokens returns 0 for missing ledger" {
99
+ local total
100
+ total=$(governor_ledger_total_tokens "no-such-session")
101
+ [ "$total" = "0" ]
102
+ }
103
+
104
+ @test "governor_ledger_total_cost returns 0 for missing ledger" {
105
+ local total
106
+ total=$(governor_ledger_total_cost "no-such-session")
107
+ [ "$total" = "0" ]
108
+ }
109
+
110
+ @test "governor_ledger_call_count returns 0 for missing ledger" {
111
+ local count
112
+ count=$(governor_ledger_call_count "no-such-session")
113
+ [ "$count" = "0" ]
114
+ }
115
+
116
+ @test "governor_ledger_append writes a record and it is readable" {
117
+ local record
118
+ record=$(_make_record 5000 0.045)
119
+ governor_ledger_append "$SID" "$record"
120
+
121
+ local ledger_path
122
+ ledger_path=$(governor_ledger_path "$SID")
123
+ [ -f "$ledger_path" ]
124
+
125
+ local lines
126
+ lines=$(awk 'END{print NR}' "$ledger_path")
127
+ [ "$lines" = "1" ]
128
+ }
129
+
130
+ @test "governor_ledger_total_tokens sums across multiple records" {
131
+ governor_ledger_append "$SID" "$(_make_record 3000 0.027)"
132
+ governor_ledger_append "$SID" "$(_make_record 4000 0.036)"
133
+ governor_ledger_append "$SID" "$(_make_record 2500 0.023)"
134
+
135
+ local total
136
+ total=$(governor_ledger_total_tokens "$SID")
137
+ [ "$total" = "9500" ]
138
+ }
139
+
140
+ @test "governor_ledger_total_cost sums across multiple records" {
141
+ governor_ledger_append "$SID" "$(_make_record 1000 0.01)"
142
+ governor_ledger_append "$SID" "$(_make_record 1000 0.02)"
143
+
144
+ local total
145
+ total=$(governor_ledger_total_cost "$SID")
146
+ # Allow slight floating-point representation variance
147
+ [[ "$total" =~ ^0\.03 ]]
148
+ }
149
+
150
+ @test "governor_ledger_call_count counts records" {
151
+ governor_ledger_append "$SID" "$(_make_record 1000 0.009)"
152
+ governor_ledger_append "$SID" "$(_make_record 1000 0.009)"
153
+ governor_ledger_append "$SID" "$(_make_record 1000 0.009)"
154
+
155
+ local count
156
+ count=$(governor_ledger_call_count "$SID")
157
+ [ "$count" = "3" ]
158
+ }
159
+
160
+ @test "governor_ledger_is_poisoned returns false for healthy ledger" {
161
+ governor_ledger_append "$SID" "$(_make_record 1000 0.009)"
162
+ run governor_ledger_is_poisoned "$SID"
163
+ [ "$status" -ne 0 ]
164
+ }
165
+
166
+ @test "governor_ledger_is_poisoned returns true after poison sentinel" {
167
+ local ledger_path
168
+ ledger_path=$(governor_ledger_path "$SID")
169
+ mkdir -p "$(dirname "$ledger_path")"
170
+ touch "$(governor_ledger_poison_path "$ledger_path")"
171
+
172
+ run governor_ledger_is_poisoned "$SID"
173
+ [ "$status" -eq 0 ]
174
+ }
175
+
176
+ @test "governor_ledger_write_direct writes a record without acquiring the write lock" {
177
+ local ledger_path
178
+ ledger_path=$(governor_ledger_path "$SID")
179
+ mkdir -p "$(dirname "$ledger_path")"
180
+
181
+ local record
182
+ record=$(_make_reservation 2000 0.018)
183
+ governor_ledger_write_direct "$ledger_path" "$record"
184
+
185
+ local lines
186
+ lines=$(awk 'END{print NR}' "$ledger_path")
187
+ [ "$lines" = "1" ]
188
+ }
189
+
190
+ @test "two-phase: reservation plus completion converges to actual tokens" {
191
+ # Phase 1: PreToolUse writes reservation with N_est = 5000
192
+ local reservation
193
+ reservation=$(_make_reservation 5000 0.045)
194
+ governor_ledger_append "$SID" "$reservation"
195
+
196
+ local mid_total
197
+ mid_total=$(governor_ledger_total_tokens "$SID")
198
+ [ "$mid_total" = "5000" ]
199
+
200
+ # Phase 2: PostToolUse writes completion with estimated=-5000, actual=4200
201
+ local completion
202
+ completion=$(_make_completion -5000 4200 0.045)
203
+ governor_ledger_append "$SID" "$completion"
204
+
205
+ # Net: 5000 + (-5000) + 4200 = 4200
206
+ local final_total
207
+ final_total=$(governor_ledger_total_tokens "$SID")
208
+ [ "$final_total" = "4200" ]
209
+ }
210
+
211
+ @test "governor_ledger_call_count excludes reservation records" {
212
+ governor_ledger_append "$SID" "$(_make_reservation 1000 0.009)"
213
+ governor_ledger_append "$SID" "$(_make_completion -1000 900 0.009)"
214
+ governor_ledger_append "$SID" "$(_make_completion -1000 800 0.009)"
215
+
216
+ local count
217
+ count=$(governor_ledger_call_count "$SID")
218
+ # 2 completions, 1 reservation — only completions count
219
+ [ "$count" = "2" ]
220
+ }