Skip to content

Commit db0c4fa

Browse files
W103.7 — Kit sync to 36f0a7a: reconcile agent-kit tools and commands (#442)
Fast-forwards ~/.agent-kit from 5095a55 (44 commits behind) and reconciles tools/*.ps1 and the two divergent command cores (reconcile.md, track.md) via Sync-Kit.ps1. Four files flagged Divergent-Skipped: kept this repository's W-vocabulary drift checker (Test-DesignDrift.ps1/.Tests.ps1) and design-state work-mirror skip guard (Read-DesignState.Tests.ps1) as deliberate, and took the kit's Measure-Session.ps1 (subagent usage accounting, worktree-safe cost-log path) since this repository's copy was merely stale, not overridden. tools/ Pester: 324 passed, 0 failed, 35 intentional skips (up from 263/0/22 before the sync pulled in new coverage). Test-Companion.ps1: Valid, 23 cores, 14 companions, 9 absent.
1 parent 10e566a commit db0c4fa

17 files changed

Lines changed: 2627 additions & 153 deletions

.claude/commands/reconcile.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ Places where the implemented structure differs from `10-design.md` — module bo
3939
## Undocumented decisions
4040
Choices made during implementation that are not in `90-decisions.md`. These are the ones that silently become load-bearing.
4141

42+
## LiveAlreadyStated
43+
For each active unit, compare every decision named in its `Live` against the artifact that unit is live on — its own `Anchor`, or a record one hop from it (`design/20-contract.md` § *The divergence classes*). Where a decision's terms already stand at a heading there with no site naming it, report the apparent match under the name `LiveAlreadyStated`. The payload is three parts: the unit id, the decision id, and the candidate site in `StatedIn`'s own `<id> § <heading>` form.
44+
45+
This is a reading — `tools/Test-DesignState.ps1` declares the id and never raises it itself, for the same reason `SemanticDisagreement` cannot: judging whether a section states a decision's terms is a model reading prose, not a check a script can run. **This pass reports and never absorbs.** Acting on a match is the caller's own step 4 of `AGENTS.md` § *Writing a design-state record* — copying the payload into the record and dropping the id from `Live` — or stating in the pull request why the terms do not stand there. That sign-off happens below, at *Then ask*, the same as every other divergence this command finds; absorbing it here instead would be running that step unattended.
46+
4247
## Invalidated assumptions
4348
Anything the design assumed that implementation showed to be false.
4449

.claude/commands/track.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,22 @@ Where the script is unavailable — no `pwsh`, or `gh` unauthenticated — say s
108108

109109
`design/30-slices.md` stays authoritative for what a slice *is*. The issue tracks whether it is *done*.
110110

111+
### Landed slices → retired
112+
113+
`design/30-slices.md` § *How this document is kept* says a slice's full body lives under `## Outstanding` only until its issue closes, at which point it retires to a row under `## Landed`. Nothing else in this command writes to that document's body — the sync above only opens and closes issues — so run the retirement in the same breath, after the issue sync above:
114+
115+
```powershell
116+
pwsh ./tools/Update-SlicesDocument.ps1
117+
```
118+
119+
Mechanical only, on the same ground as `Test-DesignDrift.ps1` above (`AGENTS.md`, *What should stop being model work* — moving a slice's body and reading an id range is set arithmetic over one file). It never opens, closes, or edits an issue — read-only against the tracker (I13) — and it never touches this document's hand-authored prose: the overview blockquote, a slice's own narrative preamble, and the "What each delivered" list are all judgement, not derivable from the tracker.
120+
121+
- Exit 0 with nothing retired — say so; this is the ordinary no-op case, not a finding.
122+
- Exit 0 with slices retired — say which ones, by number, issue, and criteria range.
123+
- Exit 2 — `gh` missing or unauthenticated, or the document's `## Outstanding`/`## Landed` markers are missing or malformed. Say what could not be read and do not report the retirement as having run.
124+
- **Read what retirement left behind.** A slice's own narrative preamble — a top-of-document overview naming it as outstanding, an `## Outstanding` section paragraph describing a set that just emptied — can go stale the moment its body is retired. That is descriptive drift, corrected on the spot in the same commit (`AGENTS.md`, *Descriptive drift is corrected where it is found*), not a decision to bring back to the user.
125+
- **This is not the mirror-refresh carve-out.** `design/30-slices.md` is not `design/state/work/` or `design/state-index.md`, so a retirement (and any prose correction alongside it) goes on a branch with a pull request, per the ordinary delegation (`AGENTS.md`, *Git and delivery*) — never committed straight to the default branch.
126+
111127
### Open items → issues
112128

113129
For each bullet under `## Open` in `design/90-decisions.md`:

.claude/kit.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"source": "https://github.com/The-Running-Dev/SubZeroDev.AgentKit",
33
"branch": "main",
4-
"commit": "5095a55c262bad431632e2c9a4d7418b833b3a16",
5-
"installed": "2026-08-30",
6-
"syncedCommit": "5095a55c262bad431632e2c9a4d7418b833b3a16"
4+
"commit": "36f0a7ab1688e3c77886f99fe6a529fab3fd080c",
5+
"installed": "2026-09-05",
6+
"syncedCommit": "36f0a7ab1688e3c77886f99fe6a529fab3fd080c"
77
}

design/90-decisions.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,3 +1412,10 @@ Context: `/track`'s final step runs `tools/Update-DesignProjection.ps1` to regen
14121412
Chosen: record the absence as intended. This repository adopts the work mirror and not the projection over it. `Update-DesignProjection.ps1` reporting `DocumentMissing` is the expected outcome of a `/track` run here, not a gate failure, and `ProjectionStale` cannot fire against a projection that does not exist.
14131413
Rejected: **Creating `design/state-index.md` so the script has a target** — rejected because a projection document is a canonical `design/` artifact, and inventing one as a side effect of a tracker sync is precisely the authoring `/track` is not allowed to do; it belongs to `/design` or `/contract` if it is wanted. **Leaving it unrecorded** — rejected because the step then reports as not-run on every future invocation with nothing saying whether that is a gap or a decision, which is the rediscovery cost this register exists to stop.
14141414
Reversibility: cheap. Adopting the projection later means writing the document with its named regions and re-running the script; nothing here forecloses it, and no committed record changes shape.
1415+
1416+
### 2026-09-05 — W103.7 kit sync to `36f0a7a`: `Measure-Session.ps1` taken from the kit, three drift-checker files kept as this repository's
1417+
1418+
Context: `~/.agent-kit` was 44 commits behind `origin/main` (last synced `5095a55`). `tools/Sync-Kit.ps1 -DryRun` reported four `Divergent-Skipped` files against the kit's current copies: `tools/Measure-Session.ps1`, `tools/Read-DesignState.Tests.ps1`, `tools/Test-DesignDrift.ps1`, and `tools/Test-DesignDrift.Tests.ps1`. The last three carry deliberate, already-decided repository content — the `W`-vocabulary drift checker (`80d374b`, `582c914`, and this run's own `10e566a` parser-defect fix) and the design-state work-mirror skip guard (`68ec922`) — and were left untouched. `Measure-Session.ps1` was different: its divergence traced to `bb97138` (Slice W50-W60, 2026-08-05), predating two kit improvements with no repository-specific override in between — per-subagent usage accounting (`36650a2`) and a worktree-safe cost-log path (`84ce80d`, resolving the exact failure this repository's own `/clean` causes by deleting worktrees after merge).
1419+
Chosen: overwrite `tools/Measure-Session.ps1` with the kit's copy; keep the other three as this repository's. Verified with a full `tools/` Pester run before and after (324/324 passing, 35 intentional skips, 0 regressions) and `tools/Test-Companion.ps1` (23 cores, 14 companions, 9 absent, `Valid`, exit 0).
1420+
Rejected: **Keep this repository's `Measure-Session.ps1` as-is** — the recommended default for a `Divergent-Skipped` file, declined here because nothing in the divergence was a deliberate override to protect; it was staleness with a real cost (silently under-reporting subagent spend, and losing cost-log rows to worktree deletion). **Take the kit's `Test-DesignDrift.ps1`/`.Tests.ps1` or `Read-DesignState.Tests.ps1`** — would silently drop the `W`-vocabulary parsing this repository's slices depend on, or reintroduce the design-state self-test false-positive `68ec922` fixed.
1421+
Reversibility: cheap — four files, each fully covered by the existing Pester suite; no schema or contract surface changed.

tools/Invoke-DoneHousekeeping.Tests.ps1

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,26 @@ Describe 'Invoke-DoneHousekeeping' {
176176
$refusal | Should -Not -BeNullOrEmpty
177177
}
178178
}
179+
180+
Context 'the default branch is already checked out in another worktree' {
181+
182+
It 'reports Stopped/CheckoutFailed instead of a false success when git checkout fails' {
183+
# main stays checked out in $repo itself; a second worktree is added on a
184+
# branch that is not ahead of main (so the UnmergedCurrentBranch stop never
185+
# triggers), then the script is run from that second worktree - its
186+
# `git checkout main` collides with main already being checked out in $repo,
187+
# exactly the "fatal: 'main' is already used by worktree at ..." case #166
188+
# reports being silently swallowed by `| Out-Null`.
189+
$repo = New-GitRepo -Path (Join-Path $TestDrive 'repo-checkout-conflict')
190+
$wt = Join-Path $TestDrive 'wt-checkout-conflict'
191+
& git -C $repo worktree add --quiet -b fix/foo $wt main | Out-Null
192+
193+
$result = & $script:ScriptPath -RepoRoot $wt -DefaultBranch main -SkipPull
194+
195+
$result.Stopped | Should -Be $true
196+
$result.Reason | Should -Be 'CheckoutFailed'
197+
$result.Detail | Should -Match "used by worktree"
198+
(& git -C $wt branch --show-current).Trim() | Should -Be 'fix/foo'
199+
}
200+
}
179201
}

tools/Invoke-DoneHousekeeping.ps1

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,25 @@ if ($currentBranch -and $currentBranch -ne $DefaultBranch) {
180180
}
181181
}
182182

183-
Invoke-Git -GitArgs @('checkout', $DefaultBranch) -WorkingDir $repoRootResolved | Out-Null
183+
$checkoutResult = Invoke-Git -GitArgs @('checkout', $DefaultBranch) -WorkingDir $repoRootResolved
184+
if ($checkoutResult.ExitCode -ne 0) {
185+
[pscustomobject]@{
186+
Stopped = $true
187+
Reason = 'CheckoutFailed'
188+
Detail = $checkoutResult.Output
189+
DefaultBranch = $DefaultBranch
190+
Pulled = $false
191+
PrunedCount = 0
192+
Candidates = @()
193+
SquashMergeCandidates = @()
194+
TipAheadOfMergedPr = @()
195+
Deleted = @()
196+
Refused = @()
197+
Stashed = $stashed
198+
StashRef = $stashRef
199+
}
200+
return
201+
}
184202
$pulled = $false
185203
if (-not $SkipPull) {
186204
$pullResult = Invoke-Git -GitArgs @('pull') -WorkingDir $repoRootResolved

tools/Measure-Session.Tests.ps1

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,27 @@ Describe 'Measure-Session -TranscriptPath' {
9595
$text | Should -Match 'foo\s+1\s+1\s+0\s+0\s+0'
9696
}
9797

98+
It 'reports subagent usage as a separate figure, not folded into the session total' {
99+
New-TranscriptFile -Name 'sess-d.jsonl' -Lines @(
100+
'{"type":"assistant","timestamp":"2026-01-01T10:00:00Z","message":{"role":"assistant","model":"claude-sonnet-5","usage":{"input_tokens":10,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":5}}}'
101+
)
102+
$subagentDir = Join-Path $script:FixtureDir 'sess-d/subagents'
103+
New-Item -ItemType Directory -Path $subagentDir -Force | Out-Null
104+
Set-Content -LiteralPath (Join-Path $subagentDir 'agent-x.jsonl') -Encoding utf8NoBOM -Value @(
105+
'{"parentUuid":null,"isSidechain":true,"agentId":"x","message":{"role":"assistant","model":"claude-sonnet-5","usage":{"input_tokens":100,"cache_creation_input_tokens":0,"cache_read_input_tokens":200,"output_tokens":50}}}'
106+
)
107+
108+
$json = (& $script:ScriptPath -TranscriptPath $script:FixtureDir) -join "`n" | ConvertFrom-Json
109+
$session = $json.sessions | Where-Object id -eq 'sess-d'
110+
111+
$session.total.input | Should -Be 10
112+
$session.total.cacheRead | Should -Be 0
113+
$session.subagents.calls | Should -Be 1
114+
$session.subagents.input | Should -Be 100
115+
$session.subagents.cacheRead | Should -Be 200
116+
$session.subagents.output | Should -Be 50
117+
}
118+
98119
It 'throws when -SessionId matches no transcript' {
99120
New-TranscriptFile -Name 'sess-c.jsonl' -Lines @(
100121
'{"type":"assistant","timestamp":"2026-01-01T10:00:00Z","message":{"model":"claude-sonnet-5","usage":{"input_tokens":1}}}'
@@ -234,6 +255,40 @@ Describe 'Measure-Session -Hook' {
234255
$rows[1] | Should -Match 'hook-sess'
235256
}
236257

258+
It 'writes a session run from a git worktree to the main checkout''s log, not the worktree''s own' {
259+
# #182: the log path used to be derived from the running script's own
260+
# location (Split-Path $PSScriptRoot -Parent), which differs per
261+
# worktree even though every worktree shares one repository. A
262+
# session run inside a worktree - this repository's own normal
263+
# /slice workflow - silently logged to <worktree>/.claude/, invisible
264+
# to anyone checking the main checkout, and lost once the worktree
265+
# was deleted. Reproduced here with a real 'git worktree add' rather
266+
# than a timing trick, since the bug is structural, not a race.
267+
$mainDir = Join-Path $TestDrive ([guid]::NewGuid().ToString())
268+
New-Item -ItemType Directory -Path (Join-Path $mainDir 'tools') -Force | Out-Null
269+
Copy-Item -LiteralPath $script:ScriptPath -Destination (Join-Path $mainDir 'tools/Measure-Session.ps1')
270+
271+
git -C $mainDir init --quiet -b main 2>&1 | Out-Null
272+
git -C $mainDir config user.email 'test@example.com' 2>&1 | Out-Null
273+
git -C $mainDir config user.name 'Test' 2>&1 | Out-Null
274+
git -C $mainDir add tools/Measure-Session.ps1 2>&1 | Out-Null
275+
git -C $mainDir commit --quiet -m seed 2>&1 | Out-Null
276+
277+
$worktreeDir = Join-Path $TestDrive ([guid]::NewGuid().ToString())
278+
git -C $mainDir worktree add --quiet $worktreeDir -b wt 2>&1 | Out-Null
279+
280+
$transcript = New-TranscriptFile -Name 'worktree-sess.jsonl' -Lines @(
281+
'{"type":"assistant","timestamp":"2026-01-01T10:00:00Z","message":{"model":"claude-sonnet-5","usage":{"input_tokens":5,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":1}}}'
282+
)
283+
$payload = (@{ transcript_path = $transcript } | ConvertTo-Json -Compress)
284+
285+
$payload | pwsh -NoProfile -File (Join-Path $worktreeDir 'tools/Measure-Session.ps1') -Hook
286+
287+
Test-Path (Join-Path $mainDir '.claude/session-costs.tsv') | Should -BeTrue
288+
(Get-Content (Join-Path $mainDir '.claude/session-costs.tsv') | Select-Object -Last 1) | Should -Match 'worktree-sess'
289+
Test-Path (Join-Path $worktreeDir '.claude/session-costs.tsv') | Should -BeFalse
290+
}
291+
237292
It 'replaces the existing row rather than duplicating it when the same session ends twice' {
238293
$transcript = New-TranscriptFile -Name 'hook-sess2.jsonl' -Lines @(
239294
'{"type":"assistant","timestamp":"2026-01-01T10:00:00Z","message":{"model":"claude-sonnet-5","usage":{"input_tokens":5,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":1}}}'

0 commit comments

Comments
 (0)