Repository navigation
Releases: beyondworks/castra
Release list
v0.9.1
Changed
- In
autoandbypassPermissionsmodes Castra no longer opens approval dialogs.
Across 356 sessions over four days its hooks asked 17 times; all 17 were approved
and ran unchanged, so the dialogs cost attention without changing any result, and
a dialog that is always approved trains the approval of the one that matters.
A would-be "ask" now reaches the model as context instead. A fact-based "deny" —
failing or unfinished CI on a release target, printing environment-variable files,
force pushes — opens no dialog and is unchanged.default,acceptEditsand
dontAskstill ask.
Fixed
- The release gate treated an echo label mentioning a tag inside a compound command
as a tag, so a read-only release lookup opened a dialog. Print-only and search
segments are dropped before looking for publication, without stripping quotes
globally, becausebash -c "git tag v1"does execute its quoted text. - Piping echo output into a shell passed the gate: echo was excluded by its first
word even though its output was executed. Segments piped onward are now kept. - Any compound command led by
cdmatched everygit push, so the common
cd repo && git push origin featurewas read as a release, andgit tag --list
or a baregit tagwas read as creating a tag. Compound commands now share one
tag-aware definition.
v0.9.0
Changed
- Ground repairs in the actual runtime and a discriminating observation; trace directly related defects through shared consumers and relevant persistence/failure/recovery transitions.
- Use representative disposable fixtures and supported runtime surfaces before delegating feasible checks back to the user. Preserve permissions, user data and honest surface/test-count boundaries.
Added
reframeandfinishrecovery modes. Normal execution already follows their outcome criteria.- Bounded shared contract loader and per-session hash: a changed contract reaches an existing registered UserPromptSubmit hook once; unchanged ordinary turns add no pack. Explicit calls restore it, while
plainopts out of injection. - Reproducible local Messenger surrogate evaluation with external functional assertions. It does not establish desktop/production correctness or model equivalence.
v0.7.0
Six rules drawn from failures observed in one session, split by whether an event
can enforce them. The three that an event can enforce became hooks; writing the
other three down is only worth doing because the pack is injected.
Added
castra-release-gate.py, aPreToolUsehook ongit tag, tag pushes, and
gh release create. It reads the CI result for the current HEAD and refuses
when a run is failing or still in flight. A tag went out on a commit whose
Windows job had not reported; the rule against that already existed, and what
was missing was looking at the fact at the moment of the act. When the result
cannot be determined — nogh, no network, no run registered yet — it asks
instead of refusing, because a gate that cannot be satisfied gets routed around.- Open-loop tracking now covers CI workflows and migrations, not just code
extensions. A workflow was edited and never run, and extension-only matching
saw nothing. Ordinary yaml stays out. - The installer records a manifest of source path and file hashes, and the
SessionStarthook reports when the installed copy has fallen behind that
source. The posture hook had been reading a stale pack while the checks
asserted against the repo, and nothing surfaced the mismatch.
Added to the pack
- Check that what you waited for is what you looked at. Match an async result
by sha, tag, or run id. The newest row is not your row — this produced a false
failure and a false pass in the same session. - A new measure gets tested before it is believed. Run it against a known-true
and a known-false case first, and ask whether it separates present from used. - Observe in parallel, and break the cheapest hypothesis first. Reading is the
cheap capability and observing is the expensive one, which is why the pull is
toward reading. Spend the reading on choosing what to observe.
Pack is now 32 sections, about 9,900 tokens per session. 6 hooks.
v0.6.0
Two sections added after a real incident: a cause was reported twice from reading
code alone, and a three-minute experiment — broadcast one event, watch the screen —
overturned it. The right conclusion was the opposite of the reported one, and the
hour spent reading had also hidden a node timeout, a storage polling load, and an
unanswered permission prompt sitting in plain view.
Added
- Reproduce first, and keep observing. Given a symptom, the first action is to
look at where the symptom lives, not at the code. A hypothesis formed from reading
is only a tool for choosing the next observation, and the observation to run is the
one that would break it. Roughly ten minutes with one hypothesis and no movement is
the cue to stop and list the layer the symptoms share. The section names why this
fails — code is reachable and a screen is not, so the shortcut feels free — because
naming it is what interrupts it. It also says to check whether a run finished before
re-running it, since a re-run can overwrite the evidence that it succeeded. - Naming where a fix reached. "Fixed" is a claim about a place: source, installed
build, resident server, live database, edge function, remote device. A change that
has not reached the user's screen is not fixed, merging is not arrival, and every
fix closes with an observation at the place the symptom was first seen.
These are rules rather than hooks because no event can observe whether a diagnosis
came from a screen or from a file. They live in the injected pack, so they are in
force without anyone opening a file — which is the only reason writing them down is
worth doing at all.
v0.5.1
Fixed
- The checks decoded hook output with the local code page on Windows.
subprocesswithtext=Trueuseslocale.getpreferredencoding(), which is
cp1252 there, so reading UTF-8 hook output raisedUnicodeDecodeError. The
0.3.1 fix covered what the hooks write; this covers what the checks read back.
0.5.0 was tagged before the Windows job reported, so this is the release that
actually passes on all three platforms.
v0.5.0
Three features had a measured call rate of zero because they were instructions
to call something rather than events. Detecting that they were idle would have
been the wrong fix. They are now events.
Added
castra-trace.py, aPostToolUsehook that records open loops from tool use
itself. Editing a code file records "changed, never observed running"; a command
that actually runs that file clears it. The hook is both the only author and the
only reader of the file, so there is no step that someone has to remember.
Documentation and config edits are excluded — a gate that fires on things nobody
needs to verify is a gate people learn to ignore.
Changed
- The pack no longer tells the model to invoke the risk classifier. A
PreToolUse
hook already classifies every command and the verdict arrives as a tool result. CLAUDE.mdno longer asks the model to write open loops or to call the guard.castra-posture.pyhonoursCASTRA_HOME, which the installer already honoured.
Without it a check could read a different pack than the one it asserts against.
Design rule this release establishes
A capability that depends on the model choosing to invoke it is not a control.
Bind it to an event, and derive its input from something that always exists — the
tool call, the transcript — never from a file another party is supposed to fill in.
v0.4.0
The pack was never being read. Measured across ten sessions where the routing
line was loaded, the number that actually opened the pack file was zero. A rule
that depends on the model choosing to read a rule is not in force.
Added
castra-posture.py, aSessionStarthook that prints the whole pack into
context. No matcher, so it also fires after a compaction — which replaces
earlier context with a summary and would otherwise drop the posture entirely.- The three commands the model has to call by hand (checkpoint, guardian,
open-loop entry) are printed alongside the pack. Their call rate was also zero
while they lived only in a referenced file. - Posture check that fails if any pack section is missing from the hook output,
so a summary can never quietly replace the pack.
Changed
- The harness is now four hooks rather than three, and the
CLAUDE.mdrouting
line says the pack is injected rather than telling the model to open it.
Cost
About 8,600 tokens per session. A summary would be cheaper and a summary is
exactly what was already in place while nothing read the pack.
v0.3.1
Fixed
- Hooks no longer die on a legacy console code page. Windows defaults to one, and
writing a non-ASCII byte there raisedUnicodeEncodeError, killing the hook
before it produced output — indistinguishable from a hook that decided to do
nothing. Hook output is now forced to UTF-8 and JSON escapes non-ASCII as well,
so it survives either way. Caught by the Windows CI job added in 0.3.0.
Added
- Open-loop check that runs the hook under a legacy encoding and fails if it dies.
v0.3.0
Windows support. Nothing about the harness was portable before this: the budget
hook was a shell script, registrations were shell form, and the guardian only
knew POSIX command shapes.
Changed
- The budget hook is now
castra-budget.py. Claude Code runs a shell-form hook
throughsh -con macOS and Linux but through Git Bash on Windows, falling back
to PowerShell when Git Bash is absent, so.shcould not work there. - Hooks register in exec form, spawning the interpreter directly with an
argument vector and no shell in between. This removes shell selection, quoting
and variable expansion from the path entirely. - The installer writes an absolute but deliberately unresolved interpreter path.
Resolving symlinks pinned a version-specific directory such as
.../Cellar/python@3.14/3.14.6/..., which disappears on the next patch upgrade
and takes every hook down without a word.
Added
install.ps1for Windows andinstall.shfor macOS and Linux, both thin
wrappers over a singleinstall.pyso every platform runs identical logic.
--dry-runreports what would change without writing.- PowerShell rules in the guardian: recursive
Remove-Item,Format-Volume,
Clear-Disk,Set-ExecutionPolicy,iwr | iex,Start-Process -Verb RunAs,
service stops, and reading.envwithGet-Content. tests/run.py, so the suite runs without a shell. CI now runs on Linux, macOS
and Windows, and installs into a throwaway config directory on each.
v0.2.0
Added
thinking-mapskill. Castra governs how work ends; this covers how it starts.
Four stages: split the subject into orthogonal axes before writing any candidate,
spread at least four per axis with the last one an inversion, cross-link items from
different axes with a written mechanism per pair, then prune against a criterion
fixed in advance. Two rules do the work — the orthogonality test that keeps axes
from collapsing, and the mechanism sentence that separates a synthesis from a list
pretending to be one. English and Korean versions ship together.- The installer places the skill in
~/.claude/skills/thinking-map/and leaves an
existing skill of that name untouched.