Repository navigation
Releases: nuroctane/nur-cli
Release list
v0.52.0
Follow-ups from the trial, measured
v0.51.0 closed the trial issues (#18); this release lands the pieces that were still owed on them (#1, #4, #9, #16), fixes two things found while measuring, and adds a provider. One commit per item.
Skill pack is a signed release asset (#9)
- First-party skills no longer ship inside the binary.
skills/bundled.jsonlists every pack nur ships itself (60 packs, 98 files);scripts/build_skill_pack.pyturns it intonur-skills.zip, which CI signs and publishes beside the binaries (nur-skills.zip,.minisig,.sha256). nur ecosystem refresh-skills(and the automatic ensure) downloads the pack for the running version, verifies it against the minisign key pinned in the source, and writes~/.nur/skills/<name>/<file>with a.bundled-pack.jsonstamp. A matching stamp is the fast path.NUR_SKILL_PACK=<dir|zip>and debug builds read a checkout instead. CI validates the manifest; the regressionskill-packdrives the zip path.
Credentials encrypted at rest on Windows (#4)
~/.nur/auth.json,provider_keys.jsonandprovider_sessions.jsonare sealed with DPAPI (this account on this machine) on top of the owner-only ACL from v0.51.0. Existing plaintext files are migrated on first read;[auth] encrypt_at_rest = falseopts out.nur auth statussays which protection applies. Regressioncredential-encryption.
macOS in the validate matrix (#16)
- The CI harness (unit suite, E2E, startup, regressions) now runs on macOS as well as Linux and Windows.
Prompt-cache numbers, measured on real routes (#1)
- Four sequential
nur runsessions of five requests each against OpenCodedeepseek-v4.1-flash: 94.3% of prompt tokens served from cache over all 20 requests, 99.2% on turns after the first (the first turn of a cold session is the only miss). The Codex route could not be measured: the account's ChatGPT subscription rejects every Codex model today, from the Codex CLI itself as well as from nur, so that number is still owed. - Measuring exposed that
NUR_PROVIDERwas read only by a hint string whilenur --helpadvertised it. It now selects the provider for one process (nur runand the TUI), resolving aliases and applying the route's defaults; an inherited value from a parent nur session is ignored with a notice. E2E 49.
Paper Instruments (Ultramarine 1 Flash)
- New route
paperinstruments(70 in the catalog): Chat Completions atapi.paperinstruments.com/v1,PAPERINSTRUMENTS_API_KEY, defaultultramarine-1-flash; aliasespaper-instruments,paper,ultramarine. Reasoning is mandatory on that gateway and the ladder islow/high/max, so the effort goes on the top-levelreasoning_effortfield and/effort mediumlands onhigh; assistant turns replayreasoning_content; every request carries the stickyx-session-idso a conversation stays on one deployment. E2E 50.
Smaller fixes
- Factory Droid: nur picks the newest
droidbuild on the machine instead of the first directory onPATHthat has one, which was routing to a stale binary and 404ing. - TUI over web terminals: on Unix the TUI no longer enables any-motion mouse tracking (
?1003), which made every pointer movement a redraw over a browser terminal or SSH.theme_setup.motion = "reduced"(orNUR_REDUCED_MOTION=1) holds ambient animation on one still frame and stops idle repaints;autopicks it inside Orca panes and SSH sessions./theme motiontoggles and saves. - sgrep is enforced rather than suggested: a
grepwhose pattern reads as a description is answered by sgrep, a grep past its default cap comes back reranked, andbashrefuses workspace searches while filters of another command's output still run. A missing sgrep binary degrades to lexical grep with an install hint. - The Linux pty driver in
tests/e2e/run_startup.pygives the child its own controlling terminal, so TUI regressions run under WSL and other harnesses that already hold a tty.
Tests
Unit suite 1246 passing; E2E 50 scenarios, startup 11, regressions 13 on Windows and WSL (Linux). The macOS job is new in this release.
Install
Prebuilt binaries below are signed; nur update verifies the signature before swapping. The skill pack is fetched on first run. Or:
irm https://raw.githubusercontent.com/nuroctane/nur-cli/main/install.ps1 | iex
curl -fsSL https://raw.githubusercontent.com/nuroctane/nur-cli/main/install.sh | bash
v0.51.0: the trial issues, fixed (#2-#17)
The trial issues, fixed
v0.43.4 went through an outside trial (#18) that filed seventeen issues. v0.50.0 took the prompt-cache half of #1 and the OpenCode Go header half of #2; this release closes out the rest, one commit per issue group so each change can be read on its own.
Edit tools and context store (#11, #14)
- One commit path for
write_file/edit_file/multi_edit/apply_patch: temp file + rename, a read ledger that refuses an edit when the file changed since the model last read it, and/undosealed with the post-edit digest. multi_editrejects an emptyold_stringand validates every edit before writing anything.apply_patchkeeps the file's line ending, anchors@@ -N,0hunks after line N, and shifts later hunks by earlier growth.edit_file's trimmed fallback trims only the needle.context_store: every per-session tool keys on the live session id, so/new,/resume, forks and subagents read the store they registered into. Its own output is never re-registered; results register only when they exceed the inline cap;peek/slicehonourmax_chars;searchheaders carry the right arguments.
Windows home, shell lifecycle, headless hygiene (#15, #13, #12)
- One
home_dir()that honoursNUR_HOME,HOMEandUSERPROFILEacross all 62 call sites; clippy bans the raw lookup. - Shell: a timed-out command may retry with a larger
timeout_msorbackground=true(the identical budget is still refused); cap raised to 10 minutes;bash -cinstead of a login shell; children spawn suspended into a kill-on-close Job Object (process group on Unix) so the whole tree dies. - Headless: no title escapes on a redirected stdout; exit codes 2 config, 3 auth, 4 provider, 5 budget/turns, 6
BLOCKED, 130 cancelled;nur run --output-format json|stream-json; one-time notices once; a start-up line naming which side layers are active and why the others are off;purposeon every usage row.
Login and provider identity (#3, #2, #4, #17)
- One canonical id per route with aliases (
chatgpt,codex,claude,opencode-go, ...) resolved in one place for config,--provider, failover and imports. An unknown id is a config error that names the closest match. nur auth loginends by asking which model to default to and says how the route bills. Failover from a subscription route to a per-token one is skipped unlessfailover_allow_paid = true.[protocols]table ("provider/model" = responses|chat|anthropic|gemini); a protocol-shaped 400 names the protocol used and the override key.- Importing another CLI's session is explicit:
nur auth import --from <codex|claude|...>with the rotating-refresh-token warning.--browseralways runs a fresh login; the silent fallback import is off by default. Credential files are owner-only on Windows too. docs/authentication.mdlists, route by route, which client identity the provider sees; login on those routes prints the notice first.
Peer mail, sandbox honesty, supply chain (#7, #6, #5)
- Peer mail defaults to
refuse; unknown policies fail closed; each mailbox owns a registration secret and spoofed letters are dropped;askreally asks; delivered letters arrive as<peer-mail authority="none">, never as the user. web_fetchto a new domain asks first in every prompting mode, including inside a parallel batch. Plan-mode shell runs a read-only allowlist and asks otherwise; the denylist parses the command instead of matching substrings; the unreadlandlockkey is gone from the docs.- Release binaries are minisign-signed in CI; the updater verifies against a public key pinned in the source and fails closed on a missing or invalid
.minisig. A lockfile pins every ecosystem component (npm/PyPI versions, full git SHAs for skill packs, plugins, dogwood, sgrep); packs and plugins are cloned at the pinned commit. New:nur ecosystem install <name>prints the pinned plan and runs only with--yes. The npm shim fetches exactly its own version over https and checks the checksum.
Skills, prompt, tool profile (#8, #10, #9)
- SKILL.md frontmatter is honoured:
disable-model-invocation: truemakes a skill user-only (no NL matcher, the skill tool refuses it);user-invocable: falsemakes it engine-only. - Project-local skills (
.nur/skills,.claude/skills,.agents/skills) are inert until/skills trustornur skills trust, recorded as a digest so an edit holds them again. Natural-language activation is now opt-in ([skills] auto_activate = true); slash and the skill tool always work. Description matching needs four shared words, not three. The repo's ownskills/tree is a discovery root only in debug builds. Upstream fixture skills and tracked sourcemaps are gone. - System prompt: memory routing appears once instead of four times; vendor commentary and bounty routing are gone; absolute NEVERs are goals; the OptMem block degrades to a one-line pointer when OptMem is absent.
[tools] profile = "core": fifteen local tools (read/list/write/edit/multi_edit/apply_patch, bash, sgrep/grep/glob, web_fetch/web_search, question, todo_write, submit_plan), no ecosystem tools or prose, session-fixed so the prompt-cache prefix holds.
Tests (#16)
Every group above landed with its own unit tests and E2E scenarios: 33 edit tools, 34 context store, 35 shell retry, 36-38 headless JSON and exit codes, 39-42 aliases / protocol override / failover, 43-45 domain gate and peer mail, 46-48 skills default-off, opt-in activation and the core profile. The full unit suite, 48 E2E scenarios, 11 startup scenarios and 11 regressions pass.
Install
Prebuilt binaries below are signed; nur update verifies the signature before swapping. Or:
irm https://raw.githubusercontent.com/nuroctane/nur-cli/main/install.ps1 | iex
curl -fsSL https://raw.githubusercontent.com/nuroctane/nur-cli/main/install.sh | bash
v0.50.0: stable prompt-cache prefix, session affinity, codemode
Prompt cache: a prefix that actually holds
The v0.43.4 trial (#1, #18) measured 16% cache hits where Codex and pi reach 96-98% on the same model. Every request now keeps the cached prefix intact:
- Static system prompt. Permission mode, the todo list and the session inventories (goal, harness, context variables, routed memory) no longer live in the system prompt. They ride as the last input item of each request, headed
[nur turn state], rebuilt per request and never persisted, so the prompt is byte-identical from the first request of a turn to the last. - Full tool roster every request, in a stable order. The task-shaped first-round profile that changed the schemas between requests is gone.
- Skill activation is a conversation item after the user message, not a prompt edit.
- Anthropic cache breakpoints sit on the prompt and tools and roll along the history; the turn-state block never carries one.
- Session affinity (#2). One stable id derived from the nur session id (so it survives
/model,/resumeand failover) is sent asx-opencode-sessionon the OpenCode Go route (gated on the host, not the provider id, soopencode-goand custom providers pointed at Go work),session_idon the Codex OAuth path andx-session-affinityon Fireworks. - A per-request status line reports the share of input read from cache.
- E2E: the fake provider now records request headers (#16); scenario 31 asserts the stable prefix across a whole turn.
Docs: new "Prompt caching" section in docs/providers.md.
codemode
A new codemode tool, modelled on Command Code's: the model writes a short JavaScript script that calls other tools in a loop, and only what the script prints or returns comes back, so three or more dependent reads cost one model round instead of one each.
- Runs in Boa, a pure-Rust engine with no filesystem, network, timer or process bindings; the tool bridge is the only way out.
- The bridge admits a nested call only when the permission layer already classifies it read-only (the same rule the approval gate uses, not a second allowlist), and never
agent,question,submit_plan,message,admission,bg,fractal,omporcodemodeitself. A script cannot write, edit, run a shell command or spawn work. - Globals:
tools.<name>(args),ALL_TOOLS,searchTools(q),describeTool(name),CWD,text(...)/console.log(...),store(k, v)/load(k),exit(value); top-levelawaitandreturnwork. - Limits: 64 nested calls, 1 MB printed output, 10M loop iterations, 1 s to 10 min deadline.
- Free in plan mode and approval-free in manual mode (it is read-only); not offered to subagents. Cargo feature
codemode, on by default. E2E scenario 32.
Docs: docs/tools.md → codemode.
Also
src/api/anthropic.rs: UTF-8 mojibake in comments repaired.- Release binary grows from 35 MB to 44 MB for the JS engine;
--no-default-featuresor droppingcodemodefrom the feature list builds without it.
v0.49.0
v0.49.0
Factory Droid: the installed CLI is the model catalog
/modelunderfactorynow lists exactly what the installeddroidbinary accepts.droid exec --helpboots the whole agent before printing (12-16 s), so the catalog is cached on disk per binary identity (~/.nur/cache/droid-models.json, keyed by path, size and mtime) and warmed in the background at startup when the active provider is Factory. A cold/modelwaits for an in-flight warm instead of launching a seconddroid; the capture timeout is 60 s.- Default model for the route is
claude-sonnet-5(every current build serves it; the docs catalog ran ahead of the CLI andclaude-sonnet-5-5was rejected by droid 0.196). - Picker loading text for Factory explains the first-run delay.
Transcript scrollbar
- The sticky prompt banner is now a pure overlay on the top two rows. The transcript body, scroll range and scrollbar track no longer change height when it appears, which was the two-row jump while dragging past a prompt.
- Drags survive what used to stop them:
Drag/Movedevents are no longer dropped while an approval or question modal is open, the mouse-mode re-arm never fires while a button is held, and the input flood cap keeps button transitions plus the newest pointer position. - Geometry: rounded subcell mapping in both directions, a two-cell minimum thumb when the track has room, and exact eighth-cell edges at both ends (the bottom edge paints the gap in background colour over a thumb-coloured cell).
- Look: one quiet rail, muted gold thumb at rest that lights on hover and brightens while dragging; the hover-widening to two columns is gone.
Modal scrolling
- Every list dialogue (
/model,/loginproviders and key forms,/plugins,/theme, sessions) draws the same fractional rail as the transcript and the rail is live: click or drag it to jump through the list; the selection follows the window and the theme picker keeps previewing. - The peek dialogue body glides like the transcript (wheel, PageUp/Down, Home/End move a target; the offset eases there on the same 16 ms cadence).
- Transcript hit-tests ignore rows under the sticky banner.
Tests
blocked-authstartup E2E tolerates the Windows rename lock while nur replacesauth.json.
v0.48.0
v0.48.0
OpenDesign provider (#69)
- New
opendesignroute:https://amr-link.open-design.ai/v1(OpenAI Chat Completions), keyOPENDESIGN_API_KEYfrom open-design.ai/cloud/api-keys, defaultdeepseek-v4.1-flash. API calls spend the same Design Plan / plan model credits as the desktop app. Theapi.amr.devhost quoted on the public docs page does not resolve; the console-derived link host is what works. - Docs, README and the nuroctane
/clipage now quote 69 providers.
Factory Droid: live model list
- The
factoryroute no longer serves a hardcoded snapshot. Nur runsdroid exec --help(20 s timeout, 10-minute cache) and reads the "Available Models" table, so standard-tier models such asclaude-sonnet-5andgpt-5.6-solappear next to the Droid Core ids. The documented catalog remains the fallback when the binary is missing. - "Invalid model" from the droid CLI now heals to a live sibling instead of failing the turn.
TUI scrolling
- Transcript wheel and page keys move a scroll target that the viewport eases toward (16 ms steps, a third of the remaining distance), so a flick that lands many events in one frame is one motion, not one jump. Thumb drag, End and streaming follow stay immediate.
- The sticky prompt header no longer toggles on a one-line scroll (it was computed from the uncovered viewport, producing a 3-row jump).
/loginauth-method switcher and the Jev slots list coalesce wheel events to one step per 45 ms, matching every other picker.
Compaction and search
- Compaction summaries carry running background jobs (model-launched
notify_modeljobs) so a compacted context still knows what is in flight, matching the unreal-agent carry-forward behaviour. Jev-scored compaction remains the first path when an engine is available. greptool description now tells models to callsgrepfirst for intent queries ("what the code does, not what it is called").
v0.47.0
sgrep: semantic code search, first in line for every model
- New
sgreptool backed by Context.dev sgrep (local, CPU, no API). Discover mode ranks the workspace by a plain-English query (sgrep "<query>" . -n N --json, exact identifier hits first); rerank mode (patternset) pipesrg --json -C 3throughsgrep --stdinso a broad grep comes back as the few passages that match the intent. sgrepsits beforegrep/globin the tools list and the SEARCH policy now reads "sgrep first, then grep/glob; never the shell" for all backends.- Auto-installed into
~/.cargo/binbyinstall.ps1/install.sh, bootstrap andnur ecosystem ensure(cargo install --git ... --locked, retried at most weekly), with the 32 MB model warmed ahead of the first turn.NUR_SGREP_BINoverrides discovery;nur doctorreports it. - Missing binary, timeout or failure degrade to the lexical grep path with a one-line note, never an error. Broad patterns past the 8 MiB rerank budget are cut on an event boundary and marked instead of stalling. E2E 30.
Jev engines: AMOS 1 (keyless), Interfaze 1.1 Lite, Experiential
- AMOS 1 (amos.scira.ai, Apache-2.0, four sizes 0.5B-27B) speaks System One natively and checks no API key. It is the first
key_optionalhosted engine:nur jev use --engine amos-1-miniworks with nothing stored, no Authorization header is sent on an empty slot,/authshows the row as "no key needed" (Residence::Keyless), and a key saved anyway is still honoured. - Interfaze 1.1 Lite bridges through the
openaiprotocol:nur jev start --engine interfaze-1-litereadsINTERFAZE_API_KEY(or a stored key);nur jev startnow defaults--key-envfrom the catalog likenur jev usedoes. - jev-via-experiential serves TypeSafe
jev-latestfrom Experiential Labs' own/v1/systemone, sharing theexperientialchat key. - Catalog grows to 139 engines;
nur jev infonow describes keyless engines and catalog-complete bridges accurately.
Providers
- Experiential Labs joins as chat provider #68:
EXPLABS_API_KEY,https://api.experientiallabs.ai/v1, bare model slugs (claude-sonnet-5default), 1,100+ models behind per-model provider waterfalls, BYOK or credits.
Docs
- README,
docs/index, tools, ecosystem, providers, authentication, quickstart, tui, troubleshooting, jev-local and jev-models updated (53 tools, 68 providers, 139 engines).
Upgrade: nur update, or reinstall via install.ps1/install.sh. Run nur ecosystem ensure --force once to build sgrep and warm its model.
v0.46.0
v0.46.0 - post-switch review: lean tool results, Factory Droid OAuth, grep/glob/bg/jev fixes
A review pass over everything shipped in v0.45.x, plus the token-saving conventions from unreal-agent that nur did not have yet.
Tool results cost less
bashreturns the stdout alone.Stderr:andExit code: Nsections appear only when they carry information,(no output)when nothing was printed. No per-call header.- One bound per result:
max_output_lengthis honoured once (defaultmin(80000, tool_result_max_chars), max 400000) and headroom no longer re-spills it. The marker is...N bytes truncated; complete output in <path>.... - Timeout handoff only when the command produced output in the last 15 s; a silent command is killed and reported as timed out (new E2E scenario 29). The placeholder tells the model the result arrives on its own.
- New "Turn economics" prompt guidance: go wide with independent tool calls, never poll background work.
Background jobs
- Failed jobs keep their full output (previously only a 400-char preview). Cancelled jobs report as cancelled. Only model-launched jobs post completion notes. Bytes past the 2 MB capture cap are counted and reported.
Factory Droid
/login -> Factory Droid -> Sign in with browserimports an existingdroidsign-in (keyring presence only; nothing secret is copied).FACTORY_API_KEYstill works.- Launcher discovery:
NUR_DROID_BIN,~/.factory/bin,~/.local/bin,~/bin,%LOCALAPPDATA%\Programs\factory,/usr/local/bin,/opt/factory/bin, thenPATH. The error now distinguishes "signed in, launcher missing" from "not installed". - Whole process tree is terminated on cancel/timeout;
.cmdshims never receive the prompt on argv; idle timeout defaults to the turn budget.
grep / glob
- ripgrep:
--with-filename, NUL separators (timestamps in text no longer split a line), globs anchored atpath,**/dir/pruning, streamed and cancellable output with an 8 MiB budget, exit-2 handling. - Fallback walker: ancestor false positive in
is_hard_excludedremoved, 500-byte line clip, deterministic order, output identical to the rg path. glob: directory patterns expand todir/**; the size cap applies only to grep.
Jev engine slots
- Removing a key clears the in-memory copy too; the residence panel mirrors the client's key resolution exactly; forgetting a key clears the sessions-store copy but keeps OAuth; shared-slot deletion arms then confirms; accurate "fallback vs replaces" hint; hit-rect, compact error row and PageUp/Down fixes; one probe per refresh.
Vision
- Loop-level retry without images on any transport's media-unsupported error; media-aware compaction estimate;
image/jpgnormalised for Anthropic and Gemini; Gemini only sends accepted types;ftypbrand sniff rejects QuickTime/HEIF as mp4.
Docs updated: providers, authentication, tools, troubleshooting, README.
v0.45.2
nur v0.45.2
Patch release fixing the v0.45.1 CI failure: the run_in_shell test helper is only called from Windows-only tests, so Linux clippy flagged it as dead code under -D warnings. It is now gated cfg(all(test, windows)).
No behavior changes. All v0.45.1 content applies:
- Async shell handoff: long-running commands can hand off to
bgjobs instead of dying on timeout, with head+tail output bounding. - Jev engine slots panel in the provider modal: residence state for Jev-class keys (TypeSafe hosted, local bridge, SubQ snap, OpenRouter, catalog engines) with wired deletion buttons.
- 133-engine Jev decision catalog with auto-resolved fallback engines.
- New providers: Liquid Inference and Factory Droid (
droid exectransport). - Grep/glob/browser tool fixes; image handling fixes across providers.
Full v0.45.0/v0.45.1 notes: https://github.com/nuroctane/nur-cli/releases/tag/v0.45.1
v0.45.1
Same feature set as v0.45.0 plus the clippy fixes that let CI publish all four platform binaries.
Tools
- bash: async handoff instead of kill. A command still running at its deadline (or
background=true) moves into a bg job with its partial output and job id; the turn continues and a completion note is injected once before the next model request. Configshell_timeout_handoff(default on). Newmax_output_lengtharg; output is bounded head+tail with a blob spill for the rest. - grep / glob rewrite. Cached ripgrep discovery, nested excludes,
fixed_strings,glob,type,context,multiline,max_matches, deterministic sort, truncation markers, rg errors surfaced, Windows path-safe line splitting, and a fallback walker with the same semantics. - browser captures are cancellable; the loopback check is an exact host match.
Images
Media-priced token estimates in preflight and recovery, recovery never excerpts base64 as text, the compaction summarizer strips images, Gemini inlineData, placeholders for agent-CLI transports, Anthropic media_type gate, mime sniffing and downscale before upload.
Jev / TypeSafe
- The decision engine auto-resolves from a running local bridge,
SUBQ_API_KEYorOPENROUTER_API_KEYwhen no TypeSafe key is set; the catalog grows to 133 engines withkey_env/credential_providerslots. /authand/login: the pinned TypeSafe · Jev row shows the engine in use and where its key lives. Selecting it opens the Jev engines panel: one row per slot (TypeSafe hosted, local bridge, SubQ Snap, OpenRouter Decisions, selected catalog engine),�on the slot in use, the key's residence (env / saved key / config.toml / file / loopback), and[✕]buttons that remove only Nur-owned copies. Enter saves a key under that slot's own id.
Providers
Liquid Inference and Factory Droid (droid exec transport) added; Meta lineup re-verified; 67 providers.
Tests
1128 unit tests, E2E 28/28 (scenario 20 updated, scenario 28 added).
v0.45.0
Tools
- bash: async handoff instead of kill. A command still running at its deadline (or
background=true) moves into a bg job with its partial output and job id; the turn continues and a completion note is injected once before the next model request. Configshell_timeout_handoff(default on). Newmax_output_lengtharg; output is bounded head+tail with a blob spill for the rest. - grep / glob rewrite. Cached ripgrep discovery, nested excludes,
fixed_strings,glob,type,context,multiline,max_matches, deterministic sort, truncation markers, rg errors surfaced, Windows path-safe line splitting, and a fallback walker with the same semantics. - browser captures are cancellable; the loopback check is an exact host match.
Images
Media-priced token estimates in preflight and recovery, recovery never excerpts base64 as text, the compaction summarizer strips images, Gemini inlineData, placeholders for agent-CLI transports, Anthropic media_type gate, mime sniffing and downscale before upload.
Jev / TypeSafe
- The decision engine auto-resolves from a running local bridge,
SUBQ_API_KEYorOPENROUTER_API_KEYwhen no TypeSafe key is set; the catalog grows to 133 engines withkey_env/credential_providerslots. /authand/login: the pinned TypeSafe · Jev row shows the engine in use and where its key lives. Selecting it opens the Jev engines panel: one row per slot (TypeSafe hosted, local bridge, SubQ Snap, OpenRouter Decisions, selected catalog engine),●on the slot in use, the key's residence (env / saved key / config.toml / file / loopback), and[✕]buttons that remove only Nur-owned copies. Enter saves a key under that slot's own id.
Providers
Liquid Inference and Factory Droid (droid exec transport) added; Meta lineup re-verified; 67 providers.
Tests
1128 unit tests, E2E 28/28 (scenario 20 updated, scenario 28 added).