v0.115.0
What
Added
nika check --jsoncarries one-obvious-way lints.hints[]
rows withkind: "one-obvious-way"andcode: "one-obvious-way/NNN"
(advice starts with the rule id), the same door native-first already
had. Warnings, never errors —cleanstays true.- The next tagged binary includes the harness access class.
release.ymlbuilds--features local-infer,access-harness. The
160 KBnika-harnesscrate has been on main, API-frozen, since
2026-08-08, and was compiled out of every downloadable binary.
agent:tasks can sit on a detected harness via--access <seat>
once that tag ships. Infer-grade harness (P4 ·infer:on a seat)
stays parked.crates/nika-acpstays a quarantined workspace (the
official SDK'spreserve_ordermust never unify into the engine);
Diamond CI now runs its five batteries againstnika-harnessover
a process boundary.metalstays off (candle 0.10 kernel dies at
first token). - One-shot
infer:tasks can use a proved Codex subscription seat. The
adapter runscodex exec --jsonwith schema enforcement, rejects extra
turns or implicit tools, and records subscription quota without inventing
a token count, USD price, or responding-model claim. - Every shipped example now runs in CI, not just audits. The 53-example
pack gained a manifest-driven family gate (pack_examples_family): each
example is launched throughnika try(or a stagednika runfor the
human-gated ones) under the offline mock seat, hermetically. Skips are
explicit and counted — the seatbelt-confined git/cargo examples run as
known-fail legs that fail the gate the day they heal.
Changed
capture: structuredno longer turns a jail EPERM into a green run
(#1068). A confinement denial (cat: …: Operation not permitted/
Permission denied, asandbox-exec:/bwrap:line, status 126) is
NIKA-SEC-001in every capture mode. A program's own non-zero stays
data understructured. Seatbelt and bwrap share the same stderr table
so Linux cannot re-open the hole the macOS path already closed.- A
for_eachfailure names the item. The parent note and the
error message carry the compact item identity (and which values
recovered whenon_error: skipnulled a slot). The trace no longer
records only a count. - OpenAPI lists the live POST statuses.
GET /v1/openapi.jsonnames
400, 408, 409, 413, 415, 503, and 507 onPOST /v1/jobsnext to
200/202/401/422.info.descriptionnames thatPOST /v1/runis
absent.GET /v1/jobs/{id}/statusstays{status}only; diagnosis
lives onGET /v1/jobs/{id}and SSE. - Failed HTTP jobs name a NIKA code.
GET /v1/jobs/{id}grows an
optional{error:{code,message}}onfailed. SSE settled/refused
frames may carry the same redacted pair. Paths and secret-shaped
fields stay dropped. Succeeded jobs omiterror. - POST
/v1/jobs422 names the capture diagnosis. A parse-fatal or
check-fatal world returns{error:{code,message}}with the NIKA code
when the engine stamped one, including analysis codes (AUTH/SEC)
that live onfinding.code. Symlink and other capture refuses stay
admission_refused. Paths stay dropped. - Token-file refusal is typed.
ServerError::Credentialnow names
unreadable, not a regular file, insecure mode, or invalid material.
nika serve --bindstill prints the openssl mint and never echoes
bytes. Missing--token-fileis unchanged. Paths stay dropped. nika doctoruses the same token-file policy asnika serve. A
short, non-graphic, or symlink.nika/serve.tokenis a fail with the
openssl mint, not an owner-only green. Group/world-readable still
nameschmod 600. The row stays silent when the file is absent.nika serve --bindprints the token mint instead of an opaque
credential refusal. Missing--token-file, a short secret, or a
group/world-readable file all teach
umask 077 && openssl rand -hex 24 > .nika/serve.token && chmod 600 .nika/serve.tokenand never echo the bytes. The HTTP door still
refuses to mint a secret on its own.- The listen line names the next hop. After bind it prints
GET /health, authenticatedGET /v1/openapi.json, and
POST /v1/jobswith Bearer plus Idempotency-Key. A non-loopback
bind adds that the blast radius is every workflow in--workflows.
GET /healthJSON stays the ADR-117 identity allowlist. - Action dogfood no longer runs on tags.
install.shresolves the
latest published binary, which lags the tagged tree until release.yml
finishes. The smoke workflow now lives outside the checkout so project
discovery cannot walk into this engine'snika.yaml. An additive
working-directoryinput on the composite action is the seam. - The
localrow counts what is supported, not what is here. Under a
« this machine » header it listedollama · lmstudio · llamacpp · localai · vllmwith nothing listening on any of them — the names are
what the BINARY supports, the header promises what the MACHINE has. The
row now says how many keyless engines exist and names the probe
(nika doctor --ping).nika catalogstill names them, where naming
them is true. check --fixkeeps the filed #905 fixture YAML-parseable. The
round judge already rolled back a broken write; the under-indented
description: |body (29 of 37 files in the original wave) is now a
unit of that judge, andnika check --fixon the 7-line repro cannot
announce a repair and leave PARSE-001. A repair that cannot re-parse
still refuses; the file on disk stays YAML.permits.fs~/and$HOME/grants expand at run. A confined
execcan name~/.gitconfiginstead of one laptop's/Users/…
path, so the grant that makesgit(orpython3, or any tool that
reads its own installed state) work is one a shared workflow can
commit. Expansion uses the operatorHOME;~userand another
tree stay out, andpermits.exec: ["git"]still does not grant the
homedir — the author names~/.gitconfig.- An unfilled scaffold refuses at
check, before the spend.nika new chainlaid itsSLOTmarkers as YAML comments, which the
parser drops by construction — socheckmentioned none of them, the
run exited 0, andoutput.mdwas left holding the scaffold's own
prompt echoed back by a mock: a file that outlives the terminal line
explaining it. A comment cannot refuse, so the marker descends into
the VALUE (<SLOT: what should the model do with the gathered text?>)
where the parser sees it. Seven shipped skeletons now carry one, and
checknames each unfilled slot with its line and closes on one
pasteable command. The refusal is worded as a step, not a fault —
not a workflow yet — 1 slot to fill, then it audits— because
someone who just typednika newdid nothing wrong. Fill it and the
same file audits clean and runs; that second half is pinned too, so
the refusal can never widen into a wall. checknames aninfer:/agent:task nomodel:reaches. The
MODELS rung answered for a model it could not resolve and said nothing
at all for a model that was never declared — an empty requirement list
is the same shape as a workflow with no inference tasks. The rung now
names the task, and the author reads it before the run instead of
meeting the omission as a provider error.nika doctorstops calling price rows models (#1179). The pricing
line formatted the vendored snapshot's rule count as « 633 models »,
one command away fromnika catalog's « 69 models » — two inventories
under one word, an order of magnitude apart, both on a first session.
It now reads633 price rules · 10 providers priced. Same numbers,
each naming the facet it counts: several patterns can price one model,
and a pattern can price models this catalog never lists.- The catalog marks what this build can actually run (#1184).
nika catalogships 38 vendors and this binary carries adapters for 16;
until now the human list, thecatalog_version: 1JSON and the MCP
nika_catalogtool rendered a reachable and an unreachable vendor
identically, so the fact only arrived after the choice, at the MODELS
rung. Every provider row now carriesresolves, derived from
nika_providers::CANONICAL_IDSand never typed: the JSON emits the
field (additive within v1 — no envelope bump), the human column reads
catalog only — does not resolve in this build, and the MCP tool
description tells an agent to pick only from rows where it is true.
The word is the refusal rung's own — « wired » already names a
different set (the run registry,mockexcluded) one line above, and
a synonym there would re-create the confusionmachine_truth.rs
exists to cure.nativeno longer invitesnika model pullinto a
door that refuses atcheck. One traversal (catalog_family.rs, the
rustCI job) holds all 38 rows to the same answer across both
projections and the MODELS rung, and is theproven_byfor their 38
ledger rows. nika --jsonanswers the same next step as the terminal. The
screen's one next step is now computed once and rendered by every
projection: theNext:block, the new top-levelnextfield,
start(a one-element array where it used to carry the pre-cascade
triplenika try 01-hello·nika init·nika new), and every
inference_choice.rungs[].next. Those rungs hardcodednika new hello, so a harness reading them in a directory that already held
the file was told to run a command that refuses on--force. The one
derivation carries the audited laws too — a red sole file yields
nika check <file>, a priced one yields the--max-cost-usdcap.- The first-contact screen promises one first command.
nikaand
nika welcomeused to print the cascade's singleNext:step and,
eleven lines below it, thestart heremenu that step had replaced —
two different doors for a stranger to choose between, andnika init
offered twice. The menu is gone; the body keepsthis machine,this binary, the sample block and thelearn:line. The cascade's
hardware row now readsthis hardware · N GBand the local rung no
longer repeats the RAM, so the wordsthis machinename one thing on
the screen instead of three. 43 lines down to 38. - A gate-blocked task names the upstream that closed the gate. The
row saidgate: an edge did not admit— no edge, no upstream, no
outcome — while the trace already carriedblocked_by. It now reads
blocked · <task> settled <outcome> · no binding here admits that, so
the common case is legible: atasks.X.errorbinding admits a failure
or a skip, and a task that SUCCEEDED has no error to read. - In the v0.115 development line, the run's dataflow descends to the pure
L0nika-dataflowcrate, sonika-runtimeis
not paying its 15k wall in doc comments.nika-runtimesat at 98.6% of
the production-LOC cap, and a written pull request could not land because
branch andmainwere each under it while their merge crossed. That PR
had already compressed three explanatory doc blocks to one-liners and
still did not fit: a comment is the cheapest line to delete, so it is the
first line deleted, and the budget gets satisfied by removing exactly the
thing that makes a crate maintainable.TaskRecordand the${{ }}/
cel-subset/0.1/ jq evaluation over it are one concept and are now one
crate — 14,787 → 14,053 production LOC under the corrected gate on
currentmain. Every historical
nika_runtime::path still resolves, and the four evaluation error
classes keep their historical public constructors and match arms, plus
their wire codes (NIKA-VAR-001·-002·-004·-005·-006),
byte for byte.
The L0 jq seam refuses ambient clock/timezone, host diagnostics, and
process-control natives, including a typed-error fallback that prevents a
jq halt exception from exiting the engine. Its newly public scope and
record DTOs also follow FCI-002: authority fields are private behind safe
constructors and the public dataflow types are non-exhaustive. nika cataloggroups what runs, instead of tagging what does not.
Marking each unreachable vendor was correct and unreadable: 22 of 38
rows repeated the same suffix, so the fact a reader wanted had to be
found by scanning for its ABSENCE. LOCAL and CLOUD now hold only the
16 providers this build resolves, in the same doctrine order; the rest
sit under one new head,CATALOG ONLY (no adapter in this build · nika check refuses these). The group is the marker, said once.native
leaves the « zero key · zero network » banner it could never honour.
The--jsonand MCP projections are unchanged — a machine filters on
resolves, it does not read headings.- The
✔ MODELSgreen line no longer reads as « the run will reach
the model ». The rung judges resolution in this binary — never key
presence on this machine — and the line now says so: « key presence on
this machine not judged (advisory: check --json access_plan · the run
gate refuses NIKA-INFER-001) ». The access truth existed but was
advisory-only (check --json,explain); the sentence now names that
deferral instead of letting a green claim cover it. What the gate
covers is unchanged. - Release preparation has one transactional changelog owner and three lock families. The wave sweep delegates a retry-safe engine fold to the fragment assembler, the uniformity gate covers the standalone ACP lock, and ecosystem coherence attributes editor drift from repo to release to each registry.
Fixed
- A workflow file typed bare is a run.
nika notes.nika.yamlused to
answerunrecognized subcommand— a wall where a run was meant. A
colleague sends a.nika.yamland the person types its name the way one
opens a file. The routing is narrow on purpose: the name must end in the
workflow suffix AND be a file on disk, so a typo'd verb keeps clap's
did-you-mean and a suffix naming nothing keeps clap's error. - One pre-push gate at a time per machine — a second push waits instead
of racing. Three concurrentlefthook run pre-pushprocesses over one
30Gtarget/turned three unrelated ratchets red, none of which
reproduces on an idle machine. Two were timeouts; the third,
credential-headers, reported a credential leak that does not exist
(the header it named is listed, and the gate reads that file directly —
6/6 green idle). A security vector that cries wolf trains the next reader
to wave it through. Measured the same evening: one gate takes 210-620s,
three in parallel took 1958s and all three failed. The gate now takes an
exclusive lease (mkdir, the one atomic primitive macOS and the Ubuntu
runners agree on) whose holder records its pid so a dead owner's lease is
reclaimed rather than wedging every later push.NIKA_GATE_NO_LOCK=1opts
out, and says what that re-enables. - The pre-push lease now has a judge that runs. #1064 shipped a lease
that serialises the pre-push gate, with a self-test proving mutual
exclusion, stale reclaim, and that a live owner is never robbed — and
nothing ran it. Not CI, not even pre-push, because a hook helper has no
runner. It was a green that had never been asked a question. The
issue-proofgate, shipped in the same merge, reopened #1064 within
minutes for exactly that: a close whose proof does not exist. The first
thing it caught was its own author.gate-lockjoins the ratchet matrix
besidecredential-headersandchangelog-fragments, which are there for
the same reason one degree less severe, and it fails closed when the
self-test is missing rather than reading an absent file as a pass. nika lspwatches the host it was told to watch.--clientProcessId
was accepted by clap and discarded at the call site, and theinitialize
request's ownprocessIdwas never read either, so a client that died
without closing the pipe left the language server running forever — the
ordinary path hid it, because a clean shutdown closes stdin and the server
exits on EOF. Both channels now feed one watchdog (argv wins, the payload
covers hosts that send only that), and the server exits when its declared
parent goes, per LSPinitialize§processId.- One cwd lease for the whole CLI, so the budget tests stop failing one
run in four.set_current_diris process-global andcargo test --lib
runs a crate's tests as parallel threads in one process.nika-clihad
three independent guards — one private to the budget tests, one private to
arm fire— and a third chdir site,run --example, with no guard at
all. Three private mutexes over one global resource do not compose: a test
could hold its own lock for its whole body and still have the ground moved
under it. Measured across four CI runs of effectively identical trees (two
byte-identical),access-harness testsfailed once, always on the same
threeverbs::check::budget::tests— the ones whose assertions derive
from the current directory. Every site now takescrate::cwd, and the
race is pinned by a test that runs a chdir storm against a lease holder.
An intermittent red teaches everyone to press the button again, and that
habit is how a real red gets waved through. issue-proofjudges every close, instead of only the ones the filer
armed it for. The gate exists to refuse the lying checkmark GitHub
prints when a merge auto-closes an issue — but its trigger required the
filer to have writtenproven_by:in the body, or someone to have
remembered a label. So an issue filed before the gate existed could never
opt in, and the merge that closed it is precisely the event the gate was
built for. Measured one minute apart on one issue:skipped, then
successonce the string was added. The condition is not widened, it is
deleted — a guard that cannot be false cannot drift. The honest
non-capability close now opts out through the closer's own act (GitHub's
not_planned/duplicate, or ano-capability/question/invalid
label), which is fixture-pinned inscripts/ci/test-issue-proof.sh.
wontfixdeliberately does not waive: it is added at filing time, and
honouring it would hand the opt-out straight back to the filer.issue-proofcan now read an issue that talks aboutissue-proof. The
body parser took the first line containingproven_by:anywhere, so #1200
— the issue about this very gate — could never be closed: its body quotes
the old guard,contains(github.event.issue.body, 'proven_by:'), inside a
code fence, and the parser extracted)as the job name and reopened the
issue. Measured, not reasoned:JOB=[)]. An issue that discusses the proof
mechanism is exactly the issue most likely to report a defect in it, so the
parser has to be able to read its own subject matter. A trailer is now a
line that is only the trailer, and the last one wins — which steps past a
fenced quote, an inline backticked mention, and a prose line that happens to
wrap onto column 0..agents/**is classified as the vendored copy it is. The estate called it the authored source « mirrored downstream into nika-plugins » — the exact inverse of the clients-resync lane, which re-vendors.agents/from the plugins SSOT with--delete. The misclassification is what let two merged pull requests live only here, until the lane proposed reverting them and rolling four manifests back from 0.114.0 to 0.111.0. The rule now names the lane, the SSOT, and the gate that judges the heal.check-doc-private-itemsno longer files a broken toolchain as documentation drift. The « cargo could not answer » branch tested the line prefix, but a tool abandoning printserror: could not document …— a prefix match — so the branch could never fire in exactly the situation it described (measured: a full disk undertarget/doc-checknamed crates nobody had touched, on a clean tree). The verdict now discriminates on the failure mode: incapacity verbs route to « the tool failed to run », and only when no genuine lint diagnostic stands beside them.- A skipped task's
errornow has a proven reader.EdgeKind::FailureObservationadmits{failure, skipped}, andskippedis in that set solely so a downstreamtasks.X.errorbinding has a consumer — but only the record was pinned, never the admission. The new runtime test drives a real consumer across the seam and asserts all three legs: the consumer is admitted (success, never cancelled), the bound value is the typed record with its code intact in the exact command bytes, and the same consumer against a succeeded producer settles cancelled, so the pass-set cannot silently narrow OR widen again. - The release tooling test can no longer write into the repository that invokes it.
git -Cscopes the directory, never the index: an inheritedGIT_INDEX_FILE(orGIT_DIR/GIT_WORK_TREE) pointed its fixture commits at the caller's branch while the test printed PASS. The test now sanitizes the git environment at entry and proves on exit, from its trap, that the caller's HEAD and worktree are byte-identical to what they were. nika checknow refuses athinking:block the run would reject or
ignore. The parser validated each field's type and stopped: a
budget_tokensat or overmax_tokensaudited green, then the provider
refused the call (the reasoning share lives inside the cap), and
thinking: { enabled: true }on a seat the catalog knows cannot reason
was a dead declaration the wire dropped or 400'd. Both are MODELS-rung
findings now — the seat check fires only when the catalog positively
knows the exact model (a provider row or an exact pricing pattern), so a
newly shipped model the dated snapshot has not met keeps its benefit of
the doubt, and a templatedmodel:is judged through its declared
default or left to the run.nika checkno longer greens a plain YAML number or boolean where the
spec says string.prompt: 123rode through the parser because a plain
scalar's source text restringifies silently — the field audited clean on a
value whose type the author never checked. Every string-typed verb field
(infer/agentprompt·system·model, execshell·cwd·stdin,
commandelements,envvalues, agenttools/skillsentries, invoke
tool/workflow) now refuses a plain scalar that resolves as an
int/float/bool and teaches the quoted form ("123"). The YAML 1.1 aliases
yes/nostay strings (onlytrue/falseare booleans in this
dialect), and${{ }}templates are untouched. A sweep test walks the
closed verb key sets against a checked-in field→type table, so a new verb
field without a refusing fixture fails the suite.- A refused cleanup is no longer invisible. An
after: { x: unwind }
cleanup that failed — a permit refusal, a non-zero exit, a timeout, even
a closedwhen:gate — used to vanish without a frame: the trace read
pixel-identical to a cleanup that never fired. The lane still never
propagates (best-effort by spec), but its outcome is journaled now: one
permit_checkedframe on planeon_finallyper skipped, failed, or
timed-out cleanup, naming the error code (NIKA-SEC-004and friends) —
spec 03's « its errors are logged » guarantee, kept. - One access census, one truth — the seat in the first screen, the doctor
fix, and the refusal tail. Three surfaces each read a different source of
access truth.welcomeanddoctor --jsonclassified a machine with a
signed-in harness seat asinstalled · no inference path(the probe filled
from provider rows only, the seat rows one call away and never joined in);
the doctor per-seat fix taughtinstall: @zed-industries/claude-agent-acp@…
— the wrapper idrun --accessrefuses as retired (NIKA-1802) — and the
NIKA-1800/INFER-001 witnesses never named the seat escape hatch nor the
credential's custody. The census (nika_providers::census::AccessCensus)
joins provider rows and harness seats in ONE read; the adoption ladder gains
theseat_readyrung betweenkey_presentandreal_ready; the cascade
names seats by their live pin token (claude-code, not the wrapper id);
doctor's seat fixes name--access <seat>plus the gesture; the admission
witness says<VAR> unset in process env; and the L4 refusal render plus
nika explainappend « or use a signed-in seat:--access …» — printed
only when this machine actually has one. - The arm-room test reads the cwd under the lease that governs it.
concurrent_run_rooms_are_serialized_and_restore_the_callerobserved
the process-global cwd twice — the baseline and the restoration —
without holdingcwd::hold(), so it measured whichever sibling
happened to own the process. It went red on three consecutivemain
commits withleft=cwd::tests's chdir-storm room, while this
test's own restore had already put the process back. The production
path was never at fault:enter_roomtakes the crate lease and rides
fchdirexactly ascwd.rsdocuments. Both reads now ride the lease,
which is what makes the claim well-defined. Reproduced 2/20 by running
the test WITH the storm; 40/40 after (the full suite hides it — 12/12
green either way, which is why the red only ever surfaced in CI). - The MODELS refusal points at a surface that actually names them.
It said "16 runnable —nika doctornames them". Measured on a
virgin machine: plainnika doctornames five — the local line — and
folds the ten cloud rows into « 10 providers unconfigured »;mock
gets no row at all. A stuck user was sent to a surface that named five
of the sixteen, and only--verboseimproved on it. Both refusals now
point atnika catalog, which groups all sixteen under LOCAL and
CLOUD, and the unknown-provider one adds where the id they typed sits
(CATALOG ONLY). The claim is pinned per run:
the_named_surface_actually_names_themcounts the rows the listing
renders before that head and refuses any drift from
CANONICAL_IDS.len(). - The 15k crate-size counter was wrong for 69 of 71 crates, in both
directions at once. It guards a maintainability budget, so every line
it mis-charges gets paid for in deleted doc comments — which is how #1203
surfaced it. Three defects: braces inside string literals ended a
#[cfg(test)]module early (412 lines of test body charged to production
in one file),#[cfg(test)] mod foo;— an attribute on a declaration —
swallowed whichever block came next and hid production lines, and a
phantom trailing line was charged once per file.nika-displaywas
over-charged 846 lines,nika-cap733;nika-clihad 31 production lines
hidden,nika-runtime65. The awk filter in_lib.shhad already learned
the string-literal rule in August; three python copies never did. The
counter now lives in one proven file (scripts/ci/prod-loc.py) that
refuses to render a verdict unless its own fixtures pass, and the gate
fails closed if they are missing. Corrected, the crate nearest the wall is
nika-checkat 14,697.
Security
- A decided human gate stays decided.
--resumeof a trace that
already journaled anika:promptsuccess now refuses--answeron
that same task (environment class). A recorded NO cannot be flipped
into a shipment. A paused gate (notask_completedyet) still
accepts--answer. Measured againstnika 0.114.0 (80d62b8f8)on
human-gated-ship. - The default sandbox arm stays fail-closed (#822).
NIKA_SANDBOX
unset (auto) plus apermits:workflow plus no OS jail is NIKA-1710,
not a composed Noop run. The composition gate is tested with a Noop
decision so deleting the refuse arm cannot hide behind a host that
ships Seatbelt. Doctor display of the row stays S3. - A sanctioned secret into
agent:is still a named flow (#1041).
JOURNEY takes the agent'stools:intersected withpermits.net.http
as the destination set. SECRETS stops saying « no declared secret
reaches an effect » after the author applied the tool's ownegress:
advice. A sanction authorizes a flow; it does not deny the flow exists. - The first screen cannot promise a seat whose adapter is absent. A
signed-in Claude Code no longer reads as a runnable harness seat when
claude-agent-acp— a different npm package, the binary a session
spawns — is not on PATH.readynow needs three things, not two: the
app is here, it is signed in, and its ACP adapter answers. The row
stays visible and names the one command that closes the gap
(npm i -g @zed-industries/claude-agent-acp); hiding an app the person
has would trade one lie for another.doctor --jsonstops reporting
ready: trueandchosen_access: claude-agent-acpon a machine where
no such binary exists — an agent reads that field and acts on it.
Install
brew install supernovae-st/tap/nika # macOS · Linux
curl -LsSf https://nika.sh/install.sh | sh # script install
docker run --rm ghcr.io/supernovae-st/nika:0.115.0 --versionTarballs below: macOS arm64 / x64 · Linux x64 / arm64, plus SHA256SUMS.
Verify: three independent proofs
# 1 · checksum: the bytes you hold are the bytes CI hashed
sha256sum -c SHA256SUMS --ignore-missing # macOS: shasum -a 256 -c
# 2 · attestation: GitHub-signed build provenance for this exact artifact
gh attestation verify nika-<platform>-0.115.0.tar.gz --repo supernovae-st/nika
# 3 · SLSA provenance: the intoto asset, verifiable offline
slsa-verifier verify-artifact nika-<platform>-0.115.0.tar.gz \
--provenance-path multiple.intoto.jsonl \
--source-uri github.com/supernovae-st/nika --source-tag v0.115.0Provenance
Built from tag v0.115.0 by release.yml
on GitHub-hosted runners. Provenance is published twice: GitHub's native
build attestation (proof 2) and the SLSA generator's multiple.intoto.jsonl
release asset (proof 3). The release itself is a claim on the
machine-verified timeline: https://nika.sh/timeline
What's Changed
- fix(serve): teach the token mint and isolate action dogfood by @ThibautMelen in #1151
- fix(cli): doctor refuses tokens serve would refuse by @ThibautMelen in #1152
- fix(serve): name the nika code on failed jobs by @ThibautMelen in #1154
- fix(serve): name the nika code on admission refuse by @ThibautMelen in #1155
- fix(serve): type token-file refusal without leaking paths by @ThibautMelen in #1156
- fix(serve): stamp check-fatal nika codes on capture 422 by @ThibautMelen in #1159
- feat(cli): ship access-harness in the downloadable binary by @ThibautMelen in #1158
- fix(serve): list live post statuses in openapi by @ThibautMelen in #1161
- fix(pack,display): unblock the only required check — pack drift, then the ascii leak behind it by @ThibautMelen in #1190
- fix(cli): refuse --answer on a settled resume gate by @ThibautMelen in #1160
- fix(check): name the ancestor ceiling on the run plan by @ThibautMelen in #1166
- fix(check): give the models rung a spec code by @ThibautMelen in #1167
- fix(check): expose one-obvious-way lints on check json by @ThibautMelen in #1168
- fix(check): say lift does not open the trifecta by @ThibautMelen in #1169
- fix(runtime): name the for_each item that failed by @ThibautMelen in #1172
- test(cli): traverse the provider, template and verb families (G3's 77) by @ThibautMelen in #1191
- fix(wiring): the silent-drop watches point at their own issues by @ThibautMelen in #1197
- fix(exec): keep confinement eperm out of structured capture by @ThibautMelen in #1162
- chore(deps): Bump the github-actions group across 1 directory with 7 updates by @dependabot[bot] in #1116
- chore(deps): Bump the rust-minor-patch group across 1 directory with 7 updates by @dependabot[bot] in #1144
- fix(check): advise --fix only where --fix has something to apply by @ThibautMelen in #1199
- build(ci): one changelog fragment per change, assembled at the tag by @ThibautMelen in #1185
- fix(cli): keep check --fix from writing broken yaml by @ThibautMelen in #1163
- fix(runtime): fail closed when permits meet a missing jail by @ThibautMelen in #1164
- fix(check): name sanctioned agent secret flows on the journey by @ThibautMelen in #1165
- fix(release): the fold stages the deletions it makes by @ThibautMelen in #1201
- fix(cli): the first screen stops promising what this machine cannot run by @ThibautMelen in #1202
- fix(lsp): watch the host the server was told to watch by @ThibautMelen in #1206
- fix(catalog): a count that does not project from the binary disappears from the surface by @ThibautMelen in #1205
- fix(ci): pin the public-api rustdoc renderer by @ThibautMelen in #1213
- fix(cli,check): one next step, and a scaffold that can refuse by @ThibautMelen in #1210
- fix(corpus): execute every shipped template by @ThibautMelen in #1214
- feat(access): seat infer on attested harnesses by @ThibautMelen in #1211
- fix(check): teach the next actionable fact by @ThibautMelen in #1215
- fix(ci): wire issue 1198 to its live proof by @ThibautMelen in #1221
- fix(catalog): the group is the marker, and the pointer names a surface that names them by @ThibautMelen in #1222
- ci(spec-pin-heal): install the sandbox backend the run tests need by @ThibautMelen in #1224
- fix(access): close P4 post-merge verification gaps by @ThibautMelen in #1225
- fix(runtime): expand portable home grants at dispatch by @ThibautMelen in #1226
- W5 · the run's dataflow descends, and the three instruments that were measuring it wrong by @ThibautMelen in #1220
- fix(ci): one rule, one reader for the test-item scope by @ThibautMelen in #1227
- fix(infer): send vision and edit image fields on the wire by @ThibautMelen in #1171
- fix(ci): a proof marker is a declaration, not a mention by @ThibautMelen in #1230
- fix(runtime): make dataflow admission call-owned by @ThibautMelen in #1231
- fix(cli): read the cwd under the lease that governs it by @ThibautMelen in #1235
- The pre-push lease gets a judge that actually runs (#1064 follow-up) by @ThibautMelen in #1228
- docs(readme): name the seat rung between local and a key by @ThibautMelen in #1238
- fix(release): recover the 0.115 tooling, with the test that can no longer write into your repository by @ThibautMelen in #1241
- fix(hygiene): discriminate the doc-private-items verdict on failure mode by @ThibautMelen in #1242
- test(runtime): drive a consumer of a skipped task's error across the seam by @ThibautMelen in #1250
- fix(estate): classify .agents as the vendored copy the lane makes it by @ThibautMelen in #1251
- fix(runtime): journal the unwind outcome · a refused cleanup is visible by @ThibautMelen in #1258
- test(pack): run-gate the shipped examples corpus by @ThibautMelen in #1259
- fix(access): one census, one truth — seat rung, doctor fix, refusals by @ThibautMelen in #1261
- fix(check): the false-greens arc — plain-scalar guard, thinking laws at audit, MCP oracle parity by @ThibautMelen in #1260
- chore(pack): re-vendor spec@4565b9d — the last two thinking-law examples (#1262) by @ThibautMelen in #1263
- fix(ci): the next-tag projector anchors proven_by like the issue-proof reader by @ThibautMelen in #1264
Full Changelog: v0.114.0...v0.115.0