Releases: RealDealCPA-VR/IronCore
Release list
IronCore v0.3.2
Added
- Update notifier — a gentle "a newer version is available" nudge. A new
ironcore/update.pychecks PyPI for a newer release of theironcore-cli
distribution and, when one exists, prints a one-line upgrade hint. It
never auto-installs — a CLI that runs shell commands and edits files leaves
the user in control of what version they run — so it only ever prints the
command (pip install -U ironcore-cli/uv tool upgrade ironcore-cli/
pipx upgrade ironcore-cli). The check is fail-silent (any network/DNS/
timeout/bad-JSON error reads as "no update", never a traceback), cached for
a day at~/.ironcore/update-check.json(an atomic write, so a normal launch
inside the window does not dial), short-timeout, and opt-out via a new
[update] checksetting (defaulttrue). It surfaces in exactly two places,
and neither runs in a non-interactive / headless / CI context:ironcore doctorprints[--] update: <v> available …/[ok] up to date (<v>)(only
when stdout is a real terminal; offline is never adoctorfailure), and the
TUI posts a single boot-style note in the background at startup. Headless
ironcore execand any non-TTY path never dial PyPI. Version comparison uses
packaging.version(PEP 440, pre-release aware). Documented indocs/CONFIG.md
§12, theironcore initstarter config, and the README's new "Updating"
section. 31 new offline tests (tests/test_update.py+ TUI/doctor additions),
all inject the fetch so nothing dials.
Fixed
- Update notifier: declared its
packagingdependency so a stock install's
ironcore doctorno longer crashes.ironcore/update.py'sis_newerimports
packaging.version, butpackagingwas not in the runtimedependencies— it
was only reachable in the dev env becausepytestpulls it in, so the whole
green suite hid the gap. In a stock install (pip/pipx/uv tool install ironcore-cliinto a clean env),packagingwas absent, soironcore doctor
raisedModuleNotFoundError: No module named 'packaging'and exited 1 — a
doctor failure caused solely by the notifier — and the TUI boot nudge was
silently dead.packaging>=23.0is now a declared runtime dependency.
Defense in depth:is_newer's import moved inside itstry(a future prune
degrades to "no nudge", not a crash) anddoctor's_report_updateis wrapped
fail-silent, so the maintenance ping can never be the reason a doctor run ends
in a traceback. Three offline regression tests pin the declaration and both
guards.
IronCore v0.3.1
First release published to PyPI: pip install ironcore-cli. No code change from
0.3.0 — a plumbing retry. 0.3.0's publish job ran before the PyPI Trusted Publisher
was fully configured, so it built and shipped a GitHub Release but never reached
PyPI. A published PyPI version cannot be re-uploaded once its release run has
failed, so the retry ships under a new patch number; 0.3.0's GitHub Release keeps
its attached wheels. (The distribution is ironcore-cli because PyPI refused the
bare name ironcore as too similar to the unrelated iron-core; the command and
the import are still ironcore.)
IronCore v0.3.0
The frontier-parity release. A three-lens review measured IronCore against OpenAI
Codex CLI and xAI grok-build: the engine, safety kernel and envelope stack came out
frontier-grade (a dozen attempted jail/gate/injection bypasses all failed), but the
2026 platform layer was missing. This release closes it — skills (the SKILL.md
open standard), headless ironcore exec, AGENTS.md/CLAUDE.md compatibility, and a
web_search tool — and fixes the concrete bugs the review surfaced (/loop never
ran, gitignoring .ironcore/ silently killed undo, /goal verify: didn't arm the
engine, the task wasn't auto-pinned as durable state) plus a security finding (the
verify: command now goes through the policy gate). 1903 offline tests.
Added
- Headless exec —
ironcore exec "<prompt>"(PKG-5). IronCore is now
scriptable:execruns one turn against the real engine and renders its event
stream (ironcore/headless.py) with no TUI. In the default human mode the
model's streamed text goes to stdout and every other event (tool calls,
approvals, verify/repair status, the completion line) to stderr, so
ironcore exec "…" > answer.txtcaptures only the answer;--jsonemits one
serialized event per line to stdout for a machine consumer (thecore/events
dataclasses are an additive contract). Default--mode planis read-only and
CI-safe;--moderaises it. Approvals fail closed and invent no new
decision path: the engine's ownApprovalBrokeris built withtimeout=0,
so anyaskgate (there is no human to prompt) resolves through the broker's
existing timeout-DENY, with a one-line hint on stderr. Exit codes: 0 on
TurnCompleted, 1 onTurnError, 2 on aConfigErrorduring setup.
Stays import-light likedoctor/demo/init(the engine is lazy-imported in
the handler). web_searchtool (PKG-5). A second NET tool besidefetch_url
(ironcore/tools/search.py): it queries a configurable HTML search endpoint
([tools] search_url— a SearXNG instance or the DuckDuckGo HTML endpoint, the
default) and returns the top results as text (title · url · snippet). Results
are parsed with the stdlib HTML parser (linear, no regex backtracking on
adversarial markup), capped, and secret-redacted before they reach the
model or the transcript. It inherits the NET policy untouched — registered
only whensafety.network_toolsis true (and a non-emptysearch_urlis
set), and every call ASKS even in AUTO (NET is never auto-allowed).- Skills — the
SKILL.mdopen standard (PKG-4). IronCore now discovers, surfaces and
invokes skills: a<dir>/SKILL.mdfile (YAMLname/descriptionfrontmatter over a
Markdown instruction body), the same on-disk shape Claude Code, Codex and grok-build read
— so a skill authored for any of them works here unchanged. Discovery
(ironcore/skills.py, modeled onplugins.py) scans~/.ironcore/skills/(trusted) and
the workspace's.ironcore/skills/(clone-borne, gated);[skills] compat_dirs = true
additionally reads.claude/.codex/.grok/skillsdirs for zero-setup ecosystem
compatibility. A malformedSKILL.mdis skipped with a reason, never a crash.- Surfacing: a compact catalog (name + one-liner each) rides the SYSTEM share beside
project memory, charged viaestimate_tokensagainst the measuredhonest_context—
the envelope-native twist: on a tiny-context model it degrades to top-N (or nothing)
rather than silently eating the window. The composer budget invariant is provably
unchanged (a newskills_catalog=param, default()). - Invocation (both lazy-body per the standard):
/skilllists skills and/skill <name>injects one's body into the next turn; the model reads a skill via the new
READ-riskuse_skill(name=...)tool, riding the existing tool loop / transcript / audit. - Safety: user skills are trusted like
IRONCORE.md; a project skill is confirmed
once per workspace before first use (T8, the pattern/workflowuses) and never
reaches the model-facing catalog until approved. A skill body is display text — any
script it references runs through the EXEC-gated command tool, and noverify:directive
is ever parsed out of a skill (that path stays sourced from the projectIRONCORE.md
alone). Off switch:[skills] enabled = false. A copy-ready template ships at
examples/skills/hello-skill/; the authoring guide isdocs/SKILLS.md.
- Surfacing: a compact catalog (name + one-liner each) rides the SYSTEM share beside
- Instruction-file compat + user-global memory (PKG-3). When a workspace has
noIRONCORE.md, project memory now falls back to an existingAGENTS.md,
thenCLAUDE.md(first found wins) — so a repo cloned with a frontier
instruction file gets first-run value instead of being silently ignored
(ironically the IronCore repo itself ships anAGENTS.mdthe product used to
overlook). A user-global~/.ironcore/IRONCORE.mdis composed alongside
the project file — user-global first, then project — within the same
SYSTEM-share budget, each honestly truncated so a tiny-context model degrades
gracefully rather than dropping a source silently. A lone source stays
byte-identical to before (verbatim, no labels); only when both are present are
they joined under##provenance labels. Security: the fallback widens
display memory only. Theverify:directive is still sourced from the
projectIRONCORE.mdalone (core/verify.pyreads that one file
directly) — never fromAGENTS.md/CLAUDE.md/user-global — because a verify
command executes unattended after the first edit, so a cloned repo must not be
able to arm one. Zero-config; the engine call site is unchanged. - Auto-pinned objective (engine M1). On a session's first turn the goal is
seeded from the opening prompt (a normalized one-line copy) when/goaldid
not set one first — sostate.goalis durable and the standing-context anchor
always carries a real objective instead of "Goal: (none set)". The goal is
also re-presented as a compact one-line system message on the off-cadence
turns where the full anchor is not injected, so a compaction can never leave
the model without its objective ("re-present, don't rely on recall"). No model
call is added to the hot path; the goal line and the full anchor are mutually
exclusive and share the anchor budget, so the context budget invariant is
unchanged. /goal verify:now arms the engine's in-turn stop-condition. Attached
checks are mirrored onto the durablestate.goal_verify, and the engine's
verifier prioritizes them above IRONCORE.md / auto-detect — so a check attached
via/goalgenuinely holds the turn open ("won't call itself done until it
passes") even in a workspace with nopytest/npm/cargomarkers. Matches
what SPEC §5.5 already promised./help <command>prints that command's usage. The per-command syntax
strings (/goal verify:,/workflow run,/loop 5m,/model <name>) were
registered but unreachable from inside the product —/helponly ever showed
the one-line summaries./help <name>now prints the named command's usage +
summary (with a nearest-match hint on a miss); a bare/helpstill lists the
whole index and key reference.
Fixed
/loopactually runs now. The command parsed intervals and registered a
loop, but the app implemented neitherregister_loopnorstop_loop, so
every registration fell through to "stored; runs when the session drives it"
and nothing ever executed. The TUI now drives a real loop: a registered
loop re-submits its prompt as a genuine turn on its interval (self-paced loops
re-submit when the prior tick completes), a tick never fires while a turn is
running, and/loop stopcancels the driver. Ticks ride the ordinary turn
path, so they are gated, rendered, and session-recorded like any other turn.- Gitignoring
.ironcore/no longer silently kills undo/redo. The natural
way to quiet the?? .ironcore/line ingit statusis to add.ironcore/
to.gitignore— which, on git ≥2.50, made everysnapshot()exit 1 ("paths
ignored by .gitignore … Use -f") because the shadow-index add named an ignored
path explicitly, so/undoand/redoquietly degraded to "[snapshot
skipped]". The snapshot store now detects an already-ignored.ironcoreand
drops the redundant exclude pathspec (letting.gitignoredo the excluding),
so undo/redo keep working byte-exactly. It does not use--force, which
would have started capturing the user's other ignored files. read_imagefailure reasons are visible to the model. Only the no-vision
refusal was mirrored intoToolResult.output; unsupported-format, missing-
file, too-big and missing-pathreasons lived only in.error(UI-facing),
so the model received an empty failed result and blind-retried the same
doomed call. Every failure branch now carries its actionable reason in
outputas well, so the model can self-correct.
Security
- Verify commands are gated through the deny-list before they run. A
verify:line in a cloned repo's IRONCORE.md is repo-borne, unsandboxed
execution that fires automatically after the first edit in accept-edits/auto.
Every verify command now passesclassify_commandfirst: a deny-listed
command (rm -rf /,curl | sh, …) is refused and never executed, in every
mode; a risky-pattern command (git push,sudo, …) is skipped with a note
rather than run unattended in the autonomous modes. Either way the turn fails
closed — an unverifiable turn is never reported as done (SAFETY T7).
IronCore v0.2.3
Changed
- First release published to PyPI:
pip install ironcore-cli. No code
changed from 0.2.2 — this is a plumbing retry. 0.2.2's publish job ran the
moment its tag landed, which was before the PyPI Trusted Publisher had been
registered, so the OIDC exchange was rejected and PyPI never received the
artifact. A PyPI version cannot be re-uploaded once its release run has
failed in CI, so the retry ships under a new patch number. Everything else
about 0.2.2 stands, and its GitHub Release keeps its attached wheels.
IronCore v0.2.2
Changed
- The PyPI distribution is named
ironcore-cli. PyPI refused the bare name
ironcoreas "too similar to an existing project" — its similarity guard
collapses separators, so the unrelatediron-core(Iron.io API wrappers)
occupies the same slot. Only the distribution name moved: the console script
is stillironcore, the import package is stillironcore, the repository is
stillIronCore, and no code changed.pip install ironcore-clithen
ironcore doctor. This is the first version published to PyPI; 0.2.1 and
earlier are installable from the GitHub releases and from source.
IronCore v0.2.1
The fit-for-strangers release. 0.2.0 shipped the features; a six-lens release audit
then found that the seam a newcomer actually touches was the weak part — doctor
reported all-green against a server that could not serve a token, the offline demo
never reached an installed environment, an interrupted first-run probe could brick
the next boot, and docs/SAFETY.md documented a project-config autonomy ceiling the
code did not implement. All of that is fixed and regression-tested here. The app also
got a real visual design: one palette, colour used semantically and only semantically,
and nine screenshots in the README that show what you actually get.
Added
- Slash commands can return styled text, so the report card reads its own
verdicts (CONTRACTS.md §6, additive). A handler returnsstror
rich.text.Text;strstays the default and every existing command is
unchanged.commands.plain(result)is the accessor for anything that wants
characters rather than spans. The mechanism exists because the two screens
that carry a verdict had no way to say so:/enveloperendered
SELECTEDandREJECTED (0.19 short)in exactly the same grey, on the one
image the README uses to prove the product's thesis./envelopeis now colour-coded by outcome. The rung a measurement
selected is green, a rejected rung and its shortfall are red, the floor and
the also-ran fallback recede to grey, the honest-vs-advertised context gap is
graded, and the section headings and verdict carry weight. Green means one
thing only — a real measurement cleared a bar — so an unprobed or seeded
profile shows none of it: its floor selection, its verdict and its context
ratio all render amber and grey, because nothing there has been measured yet./goal checkcolours its payoff line:Goal stop-condition METgreen,
UNMETred. The line whose entire purpose is proving "done" is a test result
used to arrive in the same grey as the ack above it.- Colour never carries meaning alone, and the plain-text card is unchanged to
the byte:render_report_card(profile)is now literally
render_report_card_text(profile).plain, so the coloured card and the ASCII
one that gets piped, redirected or pasted into a GitHub issue cannot drift.
It stays ASCII, keeps every word, and every existing test pins both views. - Safety: styled results are composed programmatically (
Text.appendwith
an explicit style) and never viaText.from_markup. A model id of
[red]evil[/]prints those characters and arms no colour — pinned by
tests/test_report_card.py::test_report_card_never_interprets_markup.
Changed
-
ironcore demo,doctorand the report card read as the same product as the
app. The TUI got a palette; everything printed outside it was still one
undifferentiated grey, with======and+-/|ASCII furniture standing in
for structure.ironcore/term.pynow carries the same palette and the same
rules the app theme states, and the two are pinned together by
tests/test_term.pyso they cannot drift apart.ironcore demorenders the session the way the TUI renders it: a real
rule under the masthead, the autonomy mode as a chip, the request in the
accent, and each tool call as a card with a risk-coloured rule down its left
edge, aREAD/WRITEchip, muted arguments, a red/green diff and a
green✓ ok/ red✗ error.verify passedandstop_reason: doneare
green because they are the evidence the run turned on.ironcore doctorcolours its marker column — green[ok], steel[--],
amber[!!], bold red[FAIL]— and dims the indented follow-up under each
finding so advice reads as attached to the line it belongs to. The wording,
the markers and the exit codes are untouched: styling is derived from the
text, so it cannot reword what doctor says. The one exception is noted under
Fixed below.- The
/envelopereport card gains an outcome column: every ladder rung
now saysSELECTED,ok, fallback,REJECTED (0.19 short)or
floor (always works)instead of leaving a barebelowas the only sign a
rung was thrown out, and the columns line up. It stays ASCII and plain-text
on purpose — it is pasted into issues and rendered by the transcript. - Colour follows the stream: on a terminal you get it, piped or redirected you
do not, soironcore doctor > report.txtis plain text and every string the
test suite pins is byte-identical.NO_COLORandFORCE_COLORare obeyed
(docs/CONFIG.md §10). Box-drawing degrades to ASCII as a
set on a stream that cannot encode it (a redirected Windows console is
cp1252), so a redirect can never fail halfway through a transcript. - The screenshot generator captures the CLI shots with colour, by asking a
piped run for exactly the byte stream an interactive terminal would get; and
it now widens the exported SVG's font stack to terminal fonts that are
actually installed, so box-drawing rasterizes as continuous lines instead of
the dotted ones Courier New produced.
-
The report card no longer invents measurements it does not have. A ladder
rung that was never scored saidREJECTED (0.95 short), which reads as a
measurement that failed; it now saysnot probed, distinct from a probe that
really ran and scored 0.0. -
The TUI has a coherent visual design. It rendered as one flat grey wall:
tool cards were dim monochrome blue with no structure, mode changes and gate
outcomes were the same colour as everything else, and the approval modal's
thickborder drew as a chunky orange checkerboard. There is now one palette
(ironcore/tui/theme.py— a cool slate ground, one ember accent, semantic
green/red/blue) registered as a realtextual.theme.Theme, so the whole app
spends design tokens instead of ad-hoc colours.Colour is semantic only, and escalates rather than decorates — the calm,
common case stays flat and the elevated case fills, so a singleWRITEcard
stands out of a wall ofREADcards and the current autonomy posture is
unmissable. Nothing is encoded in colour alone: every chip keeps its word,
every result keeps itsok/errortext, every diff line keeps its+/-,
and Rich drops colour automatically when stdout is not a TTY. Specifically:- Tool cards get a risk-coloured left rule and a faint panel (not a
border — a bordered box costs two columns per card and a scrolling column
of boxes reads as a form), a bold tool name, aREAD/WRITE/EXEC/NET
chip, dim arguments and a green/red result line. - The approval modal loses the
thickborder for a risk-colouredround
one with a border title, and gains a plain-language statement of what the
risk class actually does ("this changes files in your workspace"). Its three
actions are flat text so the diff stays the loudest thing on screen, and the
scrim is translucent so the tool card that raised the ask stays readable. - The status bar's mode chip is coloured by autonomy:
planblue,
manualgrey,accept-editsfilled amber,autofilled red. - Mode changes are announced with the same colour the chip just took.
- The transcript gets vertical rhythm between turns, and system notes have
their leading[tag]lifted out (red when the tag reports a failure). - The empty state is a three-line masthead instead of a single grey line,
and the transcript is bottom-aligned like a shell, so a short session sits
above the input rather than stranded against a void.
- Tool cards get a risk-coloured left rule and a faint panel (not a
-
Slash commands are echoed into the transcript. A command session rendered
as an unbroken column of grey results with the question missing — you could
not tell which output answered what. The typed line is now shown as your own
line above its result. Display only: slash commands are still not recorded to
the session transcript. -
The README screenshots are reshot against the new design, and four of the
nine now show a fuller session. Several shots framed a modal or a command
against a mostly empty transcript, which read as a dead app rather than as
work in progress: the approval gate and the/goalrun now open on a real
session, the safety-mode cycle is followed by the two-turn investigation
instead of a single turn, and the resume picker lists nine recorded sessions
rather than four. These are scenario changes intools/make_screenshots.py
only — every pixel is still a render of the shipping UI, and nothing renders
differently for capture.
Fixed
- Doctor's "unprobed" line no longer wraps through its own marker column. It
was a single 133-character sentence, so in any normal terminal it folded back
to column 0 and broke the one column a reader scans. It is now a finding plus
an indented remedy, the shape every other multi-line check already used. - The README no longer documents an install command that 404s. The Install
section offered a concrete versioned wheel URL
(…/releases/download/v0.2.0/ironcore-0.2.0-py3-none-any.whl) hedged with
"once a release is tagged". There are no tags and no releases, so that URL was
dead — verified 404 — and a skimmer copies the concrete command, not the hedge.
Replaced with a link to the releases page phrased to read correctly both before
and after the first release exists, so it cannot rot into a lie either way. - A git-free install path is documented.
pip install git+https://…was the
only documented path and it shells out to git to clone, which contradicted the
neighbouring claim that git is a soft dependency. Both halves are now precise:
git is soft at runtime (no snapshots, so/undo//redodo nothing) but
required by thegit+httpsform, and
`pip install https://github.com/Rea...