v0.99.0
0.99.0 - 2026-07-10
Added
nika:image_fx— deterministic artistic effects, the 26th builtin
(stdlib §Media graduate #3). Theimage editingdeferred row comes
home zero-dep: 15 op families (dither · palette · duotone · pixelate ·
halftone · grain · vignette · chromatic aberration · scanlines ·
glitch · ascii …) over a hand-rolled PNG codec (full RFC 1951 dynamic
Huffman inflate · 5-filter decode · CRC), seeded and byte-identical —
the recipe rides the PNGtEXtchunk (image_fx/v1), the artifact
sha256 rides the trace chain, and a re-run with the same inputs
idempotently skips.nika:chart— deterministic chart artifacts, the 27th builtin
(stdlib §Media graduate #4). Rows + a semantic spec compile to
byte-identical SVG (sha256 → trace chain): five closed types (bar ·
line · area_band · scatter · heatmap), typed semantics (usd ·
duration_ms · timestamp · category …),out:must end in.svg(the
attestation surface) andcompile_to: vega_litewrites the.vl.json
sibling. Zero dependencies; parity proven byte-exact across
architectures (wasm32-wasip1 ≡ aarch64).nika check --model <provider/model>— the static preview of the
run override: the envelope is re-priced AS IF the flag replaced the
file's default (per-taskmodel:still wins), so what check shows IS
what run will refuse or allow.- Egress to
outputs— the workflow boundary earns its valve. The
capture-taint law is deliberate (the provider saw the key — its
response is not provably clean), but a workflow that calls an
authenticated API and RETURNS the result had no sanctioned path: the
embeddedapi-upload-and-createtemplate failed its own audit (the
night battery's catch), documented as the one known gap. The gap
closes the way its own note asked:egress: [{ to: "outputs" }]on
the secret declassifies the workflow boundary itself — sink-only,
secret-specific, never authorizes a send, default-deny when absent
(spec 01-envelope §egress). KNOWN_GAP is empty: every embedded
template now passes its own audit, with zero exceptions. - The MODELS rung — every
model:must resolve in THIS binary. The
ladder validated tools but never models: a vendor-cataloged provider
the resolver cannot drive (azure/…) and a bare model id
(gpt-5-turbo) both audited green — the bare one even wore a
conjured price. Both are findings now (exit 2) with the fix taught
in-line; pricing refuses to price what cannot resolve (unpriced beats
conjured); the--jsonpayload carriesmodels_resolve+
model_findings[]; and the SAME law guards the MCPnika_checklane
(nika_providers::resolve_refusal— the two machine lanes cannot
disagree). nika wirelearnsopencodeandhermes— the two ecosystems
that natively read whatnika initwrites get first-class MCP wiring
(wire allnow covers 8 targets). OpenCode: project-local
opencode.json, its ownmcp.nikashape, idempotent merge. Hermes:
~/.hermes/config.yamlunder the Zed contract — create when missing,
recognize current, otherwise hand back the exact snippet and leave a
foreign YAML byte-identical.- OpenRouter calls carry the app-attribution pair (
HTTP-Referer: https://nika.sh+X-Title: Nika) — runs surface as Nika on the
public rankings instead of an anonymous key. Openrouter-profile only;
peers may 400 on surprise headers (proven both directions). - The plugin ships three slash commands (
/nika:check·
/nika:explain·/nika:new) — born under.agents/plugins/nika/
per the marketplace mirror law; the commands read the--json
payload, not the prose. nika explain --forecast— learned truth before a run. Duration,
cost and risk priors computed from YOUR local traces (.nika/traces/)
— deterministic stats, never a model call, never the network. The
honesty ladder is a type: never-run says so · one run is « last run » ·
2-4 runs earn a min–max range · p50/p90 bands are earned at n ≥ 5
(Hyndman & Fan type-7, the numpy/R default). Costs compose the≥
floor whenever unpriced spend participates — absence stays a dash,
never$0. Retried-then-passed counts as flaky, never failed; cache
hits and other-model runs are excluded from bands and named. The
section auto-appears innika explain <file>once 3 runs exist; the
flag forces it, and--jsongains a versionedforecastkey
(internally-tagged rungs — consumers tolerate unknown kinds).- The missing-input trap is taught at check time —
nika check
prints an[inputs]HINT when anika:readpath that resolves
statically (a literal, or one${{ vars.X }}with a literal default)
does not exist here; never an error — the file may appear at run
time, and anything dynamic is never guessed
(nika_schema::check::static_read_paths, the pure half). - The release tarball is funnel-gated —
scripts/ci/funnel-e2e.sh
plays the stranger's first path against the EXACT tarball about to
ship (clean HOME · offline · content asserts); a broken first-run
never uploads.
Fixed
- The run card advertises the full 64-hex chain head —
trace verifyprinted the whole sha256 while the run card truncated to 32,
so the taught receipts loop could only prefix-match. Byte-exact==
now closes it (CI-assertable). - The broken editor modeline names itself — cause, not symptom. A
weak copier de-comments the# yaml-language-server:line; YAML then
fails at the first mapping (« line 14,nika: v1») while the fault
is line 1 — repair loops chased the symptom forever (0/13 measured on
a 14B grid). Both forms now teach the fix on the offending line, and
the class is mirrored spec-side as conformance fixtures 014/015 —
writing them un-crashed the oracle's scan-failure path. - The cost floor prices the EFFECTIVE model. The delegation idiom
agents are taught (--model <p/m> --max-cost-usd <usd>) never met
the pre-start refusal — the floor was computed from the file's model
while the run used the override. The budget preflight now re-prices
the effective envelope; the mock-override preview idiom still passes. - The PNG heatmap speaks the SVG's quantized bins — the design pass
quantized the SVG cells onto the legend's 8 shared bins while the PNG
projection of the same recipe kept the continuous ramp; one shared
fill law now feeds both surfaces (every legend swatch IS a color a
cell can wear). - Fold verdicts are terminal kinds only — a journal truncated after
its opening line no longer surfacesworkflow_startedas if the
crashed run had reached a state;nika contextreads it as
honestly-unknown.
Changed
- The chart design pass — per-mode palettes, computable, never
eyeballed. The six-check validator refuted « Okabe-Ito is CVD-safe
on both modes » (4 slots outside the dark lightness band · the yellow
at 1.29:1 on white): dark becomes a SELECTED palette (same seven
hues, its own steps, all checks green against#0f1318; light
re-steps one slot, yellow → gold), series and diverging bins ride CSS
classes through theprefers-color-schemeseam (ONE byte-stable
document renders both themes), heatmap cells quantize onto the
legend's 8 shared bins with a 2px surface gap, bars cap at 24px with
a rounded data-end and square baseline, and the diverging midpoint
goes near-surface graphite in dark — « no change » must recede, never
glow. A BOTH-lists-style test guards the style block against palette
drift. nika-dap::stats::conformal_upper— the forecast's first THEOREM.
A distribution-free, finite-sample upper prediction bound (split
conformal, order-statistic form): for exchangeable runs the NEXT one
falls at or below the k-th order statistic with probability ≥ k/(n+1),
exactly k/(n+1) for continuous data (arXiv:2411.11824 Theorem 3.2). The
level arrives as a rational (the f64 route mis-computes the exact
feasibility frontier — 0.9·10 ceils to 10); nine runs earn a
guaranteed 90% bound, nineteen earn 95%. Proven by a deterministic
leave-one-out property test that COUNTS the theorem, never samples
it. Renderer wiring lands with forecast-R3.nika-dapis its own crate — the trace-forensics plane has one
home. The DAP replay server moves out ofnika-cli(the crate sat
at 98.9% of its size cap before the forecast landed), and the seams
every forensic reader shares descend with it: the tolerant NDJSON
reader (recover_events), the tamper-evidence chain walk
(chain::walk+ ONECHAIN_GENESISthe sink now imports — three
private sha256 copies unified), and the source-identity hashes.
nika-clire-exports every seam at its old path — zero behavior
change,nika dapanswers exactly as before.
What's Changed
- docs(changelog): append v0.98.0 by @github-actions[bot] in #308
- feat(nika-cli): the DAG draws itself in the terminal — real wires, never a lie by @ThibautMelen in #309
- feat(nika-cli): wire zed — context_servers with a JSONC-honest fallback by @ThibautMelen in #310
- feat(nika-cli): trace retention — bounded by default, never silently (ADR-100) by @ThibautMelen in #311
- fix(nika-cli): doctor sees zed — the wire target it could not diagnose by @ThibautMelen in #314
- feat(nika-event): task_recovered — the repair becomes readable in the kind stream by @ThibautMelen in #313
- feat(nika-cli): doctor + explain speak the display seam by @ThibautMelen in #315
- feat(nika-cli): nika context — the whole workspace truth in one call by @ThibautMelen in #312
- docs/readme mirror verbs by @ThibautMelen in #316
- docs: readme freshness, manifesto emergence, first-run feedback forms by @ThibautMelen in #317
- docs(readme): brand header — the lockup hotlinks the canonical kit by @ThibautMelen in #318
- ci(coherence): pack CONTENT pin + agents-mirror pin join the nightly by @ThibautMelen in #322
- docs(readme): the constellation — every sibling repo, one table by @ThibautMelen in #329
- fix(hygiene): vector 9 follows the org storefront — canonical tap name + the two new repos by @ThibautMelen in #336
- refactor(nika-catalog): session-2b splits along its rule families by @ThibautMelen in #326
- fix: the V-arc findings — the stranger's first path holds by @ThibautMelen in #327
- feat(nika-dap): admit to workspace — all 12 gates passed by @ThibautMelen in #325
- feat(hygiene): vector 9 gains counts parity — the vitrine tracks canon.yaml by @ThibautMelen in #337
- docs(privacy): the public surface drops its two studio-internal paths by @ThibautMelen in #343
- fix(changelog): the conformal cite says Theorem by @ThibautMelen in #340
- feat: nika:image_fx — deterministic artistic effects (stdlib §Media graduate #3) by @ThibautMelen in #338
- docs(privacy): the rules index drops its operator memory slugs by @ThibautMelen in #344
- feat(nika-chart): nika:chart builtin — deterministic chart artifacts (stdlib §Media #3) by @ThibautMelen in #339
- feat(provider): openrouter calls carry the app-attribution pair by @ThibautMelen in #347
- fix(cli): the run card advertises the full 64-hex chain head by @ThibautMelen in #348
- docs(readme): the model bench earns its line by @ThibautMelen in #350
- feat(nika-chart): the design pass — per-mode palettes, quantized cells, capped bars by @ThibautMelen in #349
- fix(nika-schema): the broken modeline names itself — cause, not symptom by @ThibautMelen in #351
- fix(nika-chart): the PNG heatmap speaks the SVG's quantized bins by @ThibautMelen in #355
- refactor(nika-dap): the trace forensics come home — the cli keeps the render by @ThibautMelen in #356
- feat(agents): the plugin grows its three slash commands by @ThibautMelen in #357
- fix(cli): the cost floor prices the EFFECTIVE model — and check previews it by @ThibautMelen in #352
- feat(cli): the MODELS rung — every model resolves in THIS binary by @ThibautMelen in #353
- feat(cli): wire learns opencode and hermes — the kit's two native readers by @ThibautMelen in #354
- feat(mcp): nika_check carries the MODELS rung — the third lane closes by @ThibautMelen in #358
- feat(nika-schema): egress to outputs — the workflow boundary earns its valve by @ThibautMelen in #359
- docs(changelog): the 0.99 preflight — the night's ships enter the record by @ThibautMelen in #360
- feat(run): the epilogue seam — and the run learns to say recovered and still-running by @ThibautMelen in #361
- chore(release): v0.99.0 — the audit tells the whole truth, the run narrates its own by @ThibautMelen in #362
Full Changelog: v0.98.0...v0.99.0