Reconciling the shipped similarity code against docs/metaharness-user-guide.md
turned up three divergences between what ADR-152 specifies, what the code does,
and what the docs promise.
1. Cost normalization (vector index 8) was inert below $0.999.
ADR-152 Table 1 specified `log10(usd + 0.001) / log10(10)` clamped to [0,1].
`Math.log10(10)` is 1, so the division was a no-op and the expression reduced
to `clamp(log10(usd + 0.001), 0, 1)`. Because log10 is negative for every
usd < 0.999, the clamp flattened that entire range to a constant 0 — including
the $0.048 baseline the plugin README records. Two harnesses differing only in
cost scored `cosine === 1`; the documented 9-dim vector was effectively 8-dim.
Replaced with an explicit log band: $0.001 -> 0, $10 -> 1, log-interpolated
between, monotonic and non-finite-safe. Exported as `normalizeCost()` with the
band in `COST_BAND_USD`.
2. The verdict bands had two encodings, and they disagreed.
`audit-trend.mjs` inlined the bands with a 0.80 `minor-drift` floor while the
user guide documented 0.85; `drift-from-history` prints that verdict, so the
two diverged in the 0.80-0.85 band. The bands now live once as
`VERDICT_THRESHOLDS` + `verdictFor()` in `_similarity.mjs`, `audit-trend.mjs`
imports them, and `similarity()` returns `verdict` beside `overall`. The
documented 0.85 floor is canonical.
Deliberate user-visible change: structural similarity in [0.80, 0.85) now
reports `moderate-drift` instead of `minor-drift`. `--threshold` comparison is
unaffected (it tests `overall`), but consumers branching on the verdict string
should be re-checked.
3. The user guide's weight table said 0.4/0.3/0.3; ADR-152 and the code both say
0.6/0.25/0.15. Corrected the guide, which was the outlier.
The ADR gains a dated Revision section recording both fixes and the behaviour
change. The §Spike-result figures are unchanged (the LEGAL/SUPPORT fixtures are
near-identical on the cost axis), so test-similarity.mjs Phase 8 keeps pinning
the original anchors; new Phase 7b/7c/7d coverage pins the corrected semantics.
Note this does NOT address the coarse-cosine limitation ADR-152 itself flags
("numerical cosine alone is too coarse"): uniform quality degradation still
reads as high similarity. Drift gates that must catch absolute loss should pair
structural similarity with the absolute gates rather than rely on --threshold.
Verification:
node plugins/ruflo-metaharness/scripts/test-similarity.mjs -> 128 passed, 0 failed (was 102)
node plugins/ruflo-metaharness/scripts/_spike-similarity.mjs -> both invariants hold
Co-authored-by: longxinjun-ai <longxinjun-ai@users.noreply.github.com>
Co-authored-by: rUv <ruv@ruv.net>
* dream(memory): #4137 AutoMemoryBridge was additive-only, never delete-synced removed/vanished source content
importFromAutoMemory() diffed new content against AgentDB by content-hash
to insert/skip, but never detected or deleted an entry whose source
heading (or whole source file) was removed — deleted memory content
stayed retrievable via semantic search indefinitely. Adds a per-source-
file key diff (reusing the existing single bulk query, zero schema
change) plus a second pass for files removed entirely from disk, each
with a false-full-wipe guard. Evaluated: 7 new discriminating tests,
baseline-fails/candidate-passes via stash isolation; full suite and tsc
clean modulo pre-existing/unrelated flakes. Adversarial critique found
and this session fixed forward the whole-file-removed gap and a vacuous
test assertion before this commit.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01UkpQrJUNdFzDJVnY2FzXh8
* dream(memory): #4137 append 2026-10-13 ledger row
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01UkpQrJUNdFzDJVnY2FzXh8
* fix(memory): #4137 bump @claude-flow/memory to 3.0.5 for the delete-sync change
CI's leaf-package-publish audit caught this: @claude-flow/memory is a
non-bundled leaf that resolves from the registry for the release train,
so a publishable source change (auto-memory-bridge.ts) must bump the
leaf's own version or the fix silently never reaches a release. Bumps
the leaf's package.json, the CLI's exact pin (dependencies +
optionalDependencies), and the matching pnpm-lock.yaml specifier.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01UkpQrJUNdFzDJVnY2FzXh8
* fix(memory): #4137 scope delete-sync to the writing memoryDir, avoid version collision with #4183
Review finding: the backend can be shared across multiple memory dirs
(e.g. the SessionStart hook's main dir vs. a worktree dir from
worktreeMemoryDir()). Without recording which memoryDir wrote an entry,
importing one dir could see another dir's same-named file/heading as
"gone" and delete it. Entries now carry metadata.memoryDir, and both
delete-sync passes (per-heading diff and whole-file-removed) only
consider entries whose memoryDir matches the importing bridge's own —
legacy entries written before this field existed (no memoryDir at all)
are therefore never delete-sync eligible. Two new discriminating tests
cover cross-dir isolation and the legacy-entry case; all 9 existing
delete-sync fixtures updated to carry memoryDir so they still exercise
the intended "same dir" path rather than being silently excluded.
Also bumps @claude-flow/memory to 3.0.6 (CLI pins + lockfile specifier)
since #4183 independently claimed 3.0.5 for the same leaf — whichever
of the two PRs lands second needs a version past the other's.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01UkpQrJUNdFzDJVnY2FzXh8
* fix(memory): #4137 revert stray codex/security/shared lockfile drift
An earlier `pnpm install` run in this session (unrelated to this PR's
own change) left pnpm-lock.yaml recording codex/security/shared at
specifier 3.0.6 while v3/@claude-flow/cli/package.json still pins them
at 3.0.5 — a mismatch that got swept into a prior commit by staging the
whole lockfile file. `pnpm install --frozen-lockfile` (what CI runs)
fails on that drift regardless of this PR's actual content. Reverted
those three specifiers back to 3.0.5 to match package.json; the memory
specifier bump to 3.0.6 (this PR's real change) is untouched. Verified
locally: `pnpm install --frozen-lockfile` now succeeds.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01UkpQrJUNdFzDJVnY2FzXh8
* chore(memory): untrack test-generated agentdb.rvf.idmap.json
Swept into the previous merge commit by running the test suite to
verify the merge; it's a fresh-run artifact (empty idToLabel/labelToId
maps), not source. Added to .gitignore alongside the other *.rvf
test-pollution exclusions in this package.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(memory): bump @claude-flow/memory to 3.0.7 for the delete-sync fix
AutoMemoryBridge.importFromAutoMemory()'s delete-sync fix in this PR
changed v3/@claude-flow/memory/src/auto-memory-bridge.ts without
bumping the package version. @claude-flow/memory is not bundled into
the release tarballs — it resolves from the registry — so publishing
the three train packages without this bump would ship the old 3.0.6
copy and the fix would reach nobody (ruvnet/ruflo#3335, #3390).
Will publish @claude-flow/memory@3.0.7 standalone once this merges.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(memory): #4137 bump CLI pins + lockfile specifier to match memory@3.0.7
761b8386 bumped the leaf's own package.json version to 3.0.7 but left
v3/@claude-flow/cli/package.json's exact pin (dependencies +
optionalDependencies) and the matching pnpm-lock.yaml specifier at
3.0.6 — the leaf-package-publish audit catches this: an exact pin of
"3.0.6" is not satisfied by workspace version 3.0.7, so a release would
resolve to an older published copy than the source in this repo.
Verified: audit script passes, `pnpm install --frozen-lockfile`
succeeds.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01UkpQrJUNdFzDJVnY2FzXh8
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: r <r@x>
* dream(swarm): #4173 TaskOrchestrator.getQueuePosition() priority-map key typo
getQueuePosition()'s local priorityOrder map used keys
critical/high/normal/low/background, but TaskPriority is
critical/high/medium/low. createTask() defaults unspecified priority
to 'medium', so the lookup always fell through to 0, ranking
default-priority tasks behind even 'low' in the taskQueuedEvent
position field. Real dispatch (getNextTask/getPriorityQueue) uses a
separate, already-correct map and was unaffected.
Fixed the map to Record<TaskPriority, number>, which also gives
compile-time exhaustiveness against this regression recurring.
Evaluated: ACCEPT. Stash-isolated baseline 278/280 (2 discriminating
failures) -> candidate 280/280, zero other regressions. tsc clean
both ways. Independent adversarial critique: CONFIRMED.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01FpoMprTZKfkjhytxvLpyTT
* dream(swarm): #4173 append 2026-10-14 ledger row
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01FpoMprTZKfkjhytxvLpyTT
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: r <r@x>
* dream(security): hooks_codemod path-containment bypass (CWE-22)
resolveTargets()'s containment check (hooks-tools.ts) used an unanchored
abs.startsWith(cwd) string-prefix comparison. An absolute path.resolve()
result is returned unchanged for an already-absolute input, so a sibling
directory whose name merely starts with the project directory's name
(e.g. cwd=/home/user/ruflo, target=/home/user/ruflo-staging/notes.ts)
passed containment and let this live, CLAUDE.md-recommended Tier-1 MCP
tool read+write outside the project root.
Anchors both call sites (file/files[] and glob-match branches) at a
path-separator boundary, matching the already-correct pattern used in
@claude-flow/security's isWithinPrefix(), @claude-flow/hooks/workers,
and @claude-flow/plugins/security elsewhere in this codebase.
4 new tests (2 discriminating, real temp-dir fs I/O; 2 non-discriminating
controls). Stash-isolated: both escape tests fail on baseline (file
genuinely written outside root), 4/4 pass on candidate. Related suites
(mcp-tools-deep, validate-input-*, codemod-engine/routing/corpus):
229/229 unchanged. tsc --noEmit byte-identical (9 pre-existing
environmental errors, unbuilt sibling packages).
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_0171ELk4aJ9SPhudJw3UnjgL
* dream(security): disclose symlink containment gap (follow-up)
Independent adversarial critique of 71a6c07d found a real, reachable,
distinct bypass of the same containment check: a symlink inside the
project root pointing outside it still escapes containment, since the
check compares the resolved-but-not-realpath'd path (CWE-59, broader
than the CWE-22 unanchored-prefix bug the prior commit fixed). Not
fixed here (separate hardening effort, would need fs.realpathSync-based
containment) — disclosed in-source per this repo's established
non-blocking-caveat convention.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_0171ELk4aJ9SPhudJw3UnjgL
* docs(dream-cycle): research gist for 2026-10-20
SOTA report for tonight's performance DEEP / security+hive-mind SCAN
cycle. Documents the implemented hooks_codemod path-containment fix,
deferred performance candidates (CacheManager.estimateSize redundant
stringify; AttentionCoordinator embedding-cache miss), the hive-mind
topology role/connection-wiring bug, and graded 2026 external research
+ competitor comparison.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_0171ELk4aJ9SPhudJw3UnjgL
* docs(dream-cycle): ledger row for 2026-10-20 (#4312/#4313)
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_0171ELk4aJ9SPhudJw3UnjgL
---------
Co-authored-by: Claude <noreply@anthropic.com>
Records tonight's #4285/#4286 (TopologyManager queen/coordinator
failover fix, ACCEPT) plus GitHub-verified fates for 10-16..10-18
(none transcribed as rows, none stale) and the trailing-7-night merge
status, per STEP 25 of the nightly Dream Cycle routine.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01Ch1XkuxbE8ALBoXqZyiA8A
removeFromRoleIndex() nulled the cached queenNode/coordinatorNode on
removal with no replacement; electLeader()'s hierarchical/centralized
branches then silently fell through to generic mesh-style ranking,
electing a leader without ever promoting its role. failoverEnabled
(default true) was read nowhere in the package. After exactly one
queen/coordinator death, getQueen()/getCoordinator() returned undefined
forever, contradicting CLAUDE.md's "queen controls workers directly"
guarantee.
Adds TopologyManager.promoteToRole(), gated behind failoverEnabled,
called from electLeader()'s hierarchical/centralized branches before
the generic fallthrough. 3 new discriminating tests: 2/3 fail on stash-
isolated baseline exactly as predicted, 1/3 (failoverEnabled:false
preserves old behavior) is a correct non-discriminating control. Full
@claude-flow/swarm suite 335/335 both ways, tsc clean both ways.
Independent adversarial critique: CONFIRMED-WITH-CAVEATS, no blocking
issues. Caveats disclosed in-source (promotion doesn't rewire
connections/adjacency itself; self-healing under the default
autoRebalance: true) and in the issue/gist.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01Ch1XkuxbE8ALBoXqZyiA8A
storePattern() adds every pattern to the HNSW index, but
consolidate()'s prune and dedup loops only removed entries from the
in-memory map and AgentDB — never from the HNSW index itself. In a
long-running process (HooksLearningDaemon calls consolidate() every
60s), orphaned points accumulate until maxElements is exhausted,
after which storePattern()'s unguarded addPoint() throws "Index is
full, cannot add more elements" permanently, even though the index
is full of logically-deleted ghosts.
Fixed with two additive hnswIndex.removePoint(id) calls alongside the
existing deleteFromStorage(id) calls. Two new discriminating tests
(prune path, dedup path) reproduce the exact failure on baseline and
pass on the candidate; independently re-verified by an adversarial
critic subagent. Full @claude-flow/hooks suite 147/147, tsc clean.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_016nZstVChPSSNBwmMUJW8tE
* dream(security): denylist gaps in validateEnv() for git/perl/ruby/java env-var hijack family
DENYLISTED_ENV_NAMES (v3/@claude-flow/cli-core/src/mcp-tools/validate-input.ts)
covered loader-hijack and the 2026-09-21 search-path-family fix, but missed
credential/arbitrary-command vars in the same git family (GIT_ASKPASS,
GIT_PROXY_COMMAND), interpreter-flag-injection vars for Perl/Ruby/Java
(PERL5OPT/RUBYOPT/JAVA_TOOL_OPTIONS/_JAVA_OPTIONS/JDK_JAVA_OPTIONS), two more
search-path vars (PYTHONHOME, CLASSPATH), and git's numbered
GIT_CONFIG_KEY_<n>/VALUE_<n>/COUNT runtime-config-injection family (added via
a new regex, not a fixed name — strictly broader than any single name).
PYTHONSTARTUP included for defense-in-depth with an honest caveat (interactive-
REPL-only, not live-exploitable through today's non-interactive execSync path).
Researched and explicitly rejected: PYTHONEXECUTABLE (cosmetic, macOS-only),
PSModulePath (single-source, Grade C evidence only).
18 new discriminating tests added to validate-input-env.test.ts, matching the
file's existing pattern. Stash-isolated: 18/18 fail against baseline, 51/51
pass on candidate. Full @claude-flow/cli suite: byte-identical 60-failure set
baseline vs candidate (zero regressions). tsc --noEmit: 0 errors.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01S1h1otMCeN5gSX8S5gC17H
* dream(security): fix-forward post-critique — correct test counts, document GIT_CONFIG_NOSYSTEM/PYTHONSTARTUP caveats
Independent adversarial critic caught: (1) an arithmetic error in this
session's own evaluation writeup (32 pre-existing tests, not 33; 19 new
tests of which 18 discriminate, not 18 flat); (2) GIT_CONFIG_NOSYSTEM's
denylist rationale was undocumented despite guidance/mod-sources.ts's git()
helper setting that same var defensively elsewhere in this repo; (3) the
PYTHONSTARTUP comment overclaimed that execSync's non-interactive child
"cannot enter" Python's interactive mode — a caller-supplied `python3 -i`
can still trigger it. All three addressed: gist corrected, source comments
now document the real rationale for both. Net security conclusion for
PYTHONSTARTUP unchanged (defense-in-depth, no new capability — the caller
already controls the full command string). Re-verified: 51/51 tests,
tsc --noEmit clean.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01S1h1otMCeN5gSX8S5gC17H
* dream(security): update LEDGER.md for 2026-10-16 (#4229/#4230)
Appends tonight's row, confirms #4053/#3977 merged via GitHub MCP tools,
confirms no run-gap for 10-12..10-15, and flags that 10-11's own PR cited
a CVE-2026-25725 that does not resolve in NVD/MITRE.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01S1h1otMCeN5gSX8S5gC17H
* dream(security): fix CI — bump @claude-flow/cli-core 3.7.3->3.7.4 (leaf-package-publish audit)
The "Plugin package install-safety" CI check's audit-leaf-package-publish.mjs
caught that this PR's source change to v3/@claude-flow/cli-core (not bundled
into the release tarballs, resolved from the registry at the version
v3/@claude-flow/cli/package.json pins) left the leaf's own package.json
version unchanged — per CLAUDE.md's "Publishing Rules" (ruvnet/ruflo#3335,
#3390), that means a release would ship the OLD registry copy and this
fix would reach nobody. PATCH bump per repo semver policy (bug fix, no API
change): 3.7.3 -> 3.7.4. Updated the three places that need to agree:
cli-core's own package.json, @claude-flow/cli's dependency pin, and
v3/pnpm-lock.yaml's specifier for that workspace link.
Re-verified: audit-leaf-package-publish.mjs clean, tsc --noEmit 0 errors,
51/51 tests pass.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01S1h1otMCeN5gSX8S5gC17H
* dream(security): fix CI — bump root claude-flow's @claude-flow/cli-core range 3.7.3->3.7.4
audit-wrapper-dep-ranges.mjs (ruvnet/ruflo#2127) caught that the root
claude-flow umbrella's own declared dependency range for the just-bumped
@claude-flow/cli-core leaf (3.7.3) no longer includes the workspace's actual
version (3.7.4) after the prior commit's bump. package-lock.json's resolved
node_modules/@claude-flow/cli-core record intentionally left untouched (still
3.7.3, with its real registry integrity hash) since 3.7.4 has not actually
been published yet — see the PR comment on the structural publish blocker.
Re-verified all 5 install-safety audit scripts locally: clean.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01S1h1otMCeN5gSX8S5gC17H
* Revert "dream(security): fix CI — bump root claude-flow's @claude-flow/cli-core range 3.7.3->3.7.4"
This reverts commit 9dae077cb3.
* dream(security): resolve CI blocker — @claude-flow/cli-core@3.7.4 is now published
A maintainer's 3.57.0 release (main advanced from 1882006a0 to b93144803
while this PR sat open) published @claude-flow/cli-core@3.7.4 to the real
npm registry, containing this exact fix (verified via `npm pack` + diff
against the source on this branch). That resolves the structural blocker
described in this PR's comment: the root claude-flow umbrella's own
dependency range can now safely bump to match, since 3.7.4 genuinely
exists — unlike three days ago, this no longer breaks root `npm ci`/
`npm install` for the rest of the CI matrix.
Re-verified for real this time (not just the static audits): ran
`node scripts/smoke-cli-npx-install.mjs` directly — packs the real CLI
tarball and does a genuine `npm install` into a scratch project — and it
now succeeds end-to-end (previously failed with ETARGET). All 5 install-
safety audit scripts clean. 51/51 tests, tsc --noEmit 0 errors.
Note: main's own source tree (v3/@claude-flow/cli-core/src/mcp-tools/
validate-input.ts) is still at the pre-fix 3.7.3 content as of this
commit — only the npm registry got the fix early, via whatever process
published 3.7.4. Merging this PR is what reconciles main's source with
what's already shipped to real users.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01S1h1otMCeN5gSX8S5gC17H
---------
Co-authored-by: Claude <noreply@anthropic.com>
`worker' is not a top-level command in 3.57.0, so the two examples in
docs/USERGUIDE.md exit 1 with "Unknown command: worker" when copied.
The command lives under `hooks worker', which the same guide already uses
further down.
The package.json bin for this CLI is registered as claude-flow-codex,
but the package itself publishes under the scoped name @claude-flow/codex.
Several usage examples told a reader to run 'npx claude-flow-codex ...',
which resolves to a different (nonexistent) unscoped package rather than
this one.
Co-Authored-By: RuFlo <ruv@ruv.net>
* dream(performance): #4214 RaftConsensus.awaitConsensus() busy-poll -> event-driven
RaftConsensus.awaitConsensus() polled this.proposals via setInterval(...,10)
even though checkConsensus() already emits 'consensus.achieved' on every
accept/reject transition. Raft is Ruflo's default hive-mind consensus
algorithm, reachable from production via UnifiedSwarmCoordinator.proposeConsensus().
Third instance of a busy-poll bug class already fixed twice (UnifiedSwarmCoordinator
#3378, MessageBus #3563).
4 new discriminating tests, stash-isolated baseline-fails/candidate-passes
(3/4, 1 correct non-discriminating control), independently reproduced by an
adversarial critic. Full @claude-flow/swarm suite 281/281 (277 existing + 4
new), tsc clean both ways. Standalone wall-clock probe: 3.639ms/30 poll-timers
-> 0.058ms/0 timers (N=30).
Also closes a 3-night research thread (10-09/10-10/10-14) on graph-analyzer.ts's
dead native-acceleration detection: direct inspection shows ruvector's real
minCut/louvainCommunities exports are plain JS, not native-accelerated, so
wiring them in would not be a performance win (REJECT, with evidence).
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01MFLiva2zVEXr2n9UTK5YgU
* dream(performance): #4214 record PR #4215 in LEDGER.md
Fill in the real PR number (was a placeholder pending PR creation) in
tonight's ledger row, and note the 10-12/10-13/10-14 ledger-transcription
gap per the established convention.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01MFLiva2zVEXr2n9UTK5YgU
---------
Co-authored-by: Claude <noreply@anthropic.com>
The go prompt (: or /) takes an action as well as a page. A name is matched
literally against the console's palette entries (id, or name containing the
typed words, or a text command keyword), never fuzzily. A read runs at once
through Host.run (ruflo CLI by argv only); a change raises the console's own
state.pending card, pinned under the page bar, and runs only on a lone y while
every row of the card is on screen (state.shownCard). Paste, Enter, "yes", a
card too tall for the window or a replaced card run nothing; n/Esc drops it.
The console's own verdict on the run is pinned for 20 s.
- tui/pinned.ts: the card and result rows
- session: palette(), answer(), pending(), cardShown(), pinned()
- parity table wording, ADR-489 phase 6, guide; key line says "ask first"
- tests/tui-actions.spec.ts with a CLI stand-in recording the argv
Co-Authored-By: RuFlo <ruv@ruv.net>
- tui/parity.ts: a recorded decision for each of the console's views
(same / partial / equivalent / claude-only); tests/tui-parity.spec.ts fails
when a view in VIEWS has no decision or no row in ADR-489
- 0 opens the main menu (its keys open entries), : or / goes to a page by
name, Esc/Backspace is Back, --view takes any page; the console terminal
opens the AI page and the Room says on one screen that it needs Claude Code
- terminal renderer: a Box's width is honoured (the board's two groups side by
side) and the board's entry labels are drawn (they were blank)
- --mouse (opt-in, SGR): click a page chip, wheel scrolls; off by default,
switched off on exit, report bytes ignored without the flag
- CLI: --view validates against the loaded bundle's page list, --mouse flag
Co-Authored-By: RuFlo <ruv@ruv.net>
The standalone terminal UI now leads each page with the same title art and
pictures the console draws, as coloured cells (Raster in the terminal kit),
in the console's looks (Classic BBS default, Orbital, plain), moving at
--fps with only the changed rows redrawn.
- tui/ansi.ts: Raster; control/escape/invisible/wide code points become
spaces, malformed or oversized cells draw nothing
- looks: --look, t key; --boot; --fps, --no-anim, --no-pictures
- focus reporting pauses the animation clock; NO_COLOR, dumb terminals,
and widths under 44 fall back to the words
- guide: flags, looks, the t key; corrects the confirm-card sentence
- ADR-489: phase 4 recorded, parity table skeleton
- README/guide SVGs regenerated from real frames (leak-checked)
Co-Authored-By: RuFlo <ruv@ruv.net>
- tui: the page bar was painted from session.state.view, which the AI page never sets, so on page 6 it kept the previous page lit; it now follows session.page (9 tests, 4 failed first)
- tui --help and the console README said keys 1-5; they are 1-6 and o c s e m a (test holds the help text)
- cli: policyTrustRoot() no longer joins an empty or relative $HOME, which put the anchor mirror and key under the working directory; falls back to the account home or refuses (2 tests, 1 failed first)
- docs: ruflo-tui-walkthrough.svg re-recorded from real frames with the AI page and its ask-first card, 6 AI marked
Co-Authored-By: RuFlo <ruv@ruv.net>
README gets a short optional pointer (npx ruflo tui, --plain) and one small animated SVG. docs/USERGUIDE.md gets a
Terminal UI (optional) section: requirements, commands, keys, exit codes, what each page shows, the AI page's ask-first
card and its Codex/OpenRouter rules, the honest limits and a pointer to the Orbital theme. Both SVGs are built from real
frames of ruflo-tui 3.56.7 in a throwaway sample project; scripts/render-tui-svg.mjs rebuilds them (CSS animation only,
no scripts or external references, prefers-reduced-motion shows the first frame).
Co-Authored-By: RuFlo <ruv@ruv.net>
Reduce vector replay from 29.6 MB to 3.84 MB through shared glyphs and per-cell animation. Retain 456 frames; verify SMIL timelines and img animation in Chrome.
Page 6. Reuses harness.send and the conversation bridge's sendTo, not a second harness. Host.spawn runs only the pinned Codex 0.162.0
release (never current or PATH), read-only, from a fixed argument allow-list; Host.httpSend sends only https POST to openrouter.ai or a
convoTargets host, never follows a redirect, and a key is read by variable name and never drawn. Enter shows a card of exactly what would be
sent; a lone y answers it only while it was drawn in full (#3983); spend cap from the provider-reported cost. e2e host matrix gets a tui leg
(keyless in CI, live gated). Not in this PR: the workspace-write toggle.
Co-Authored-By: RuFlo <ruv@ruv.net>
Read-only check: reads build/config.gypi of each better-sqlite3 copy in the
resolved tree (never loads an addon), warns when a locally compiled copy used
24.19+ headers while the runtime is 24.19-24.21 (nodejs/node#65446), with the
documented workarounds. Prebuilt copies carry no config.gypi and pass.
Co-Authored-By: RuFlo <ruv@ruv.net>
ADR-490 (loop), ADR-491 (mods host integration, Proposed). The loop's first run found two defects, fixed test-first:
- ruflo mcp start advertised resources but answered resources/* with -32601 (3 stdio servers).
- init --dual's marketplace clone ran under a 20 s budget (17.5 s measured), swallowing the real error.
Co-Authored-By: RuFlo <ruv@ruv.net>
Records tonight's PR #4053 / issue #4052, scores the prior night's gist
(2026-10-10, performance: 10/10), and carries forward the trailing-PR
fate summary per the established STEP 1/STEP 25 convention.
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01HW4yuGGcrCy4CcPgQ7Q2dx
Closes the gap #3835 itself disclosed: the settings-risk scanner
(CVE-2025-59536-class detection, #3043) ran at init/--upgrade and (pending
#3835) the explicit hooks_session-restore MCP/CLI handler, but never on the
one path that fires automatically on every real Claude Code session boot
-- hook-handler.cjs's session-restore command, wired via .claude/settings.json's
own SessionStart hooks array. Validated against a real 2026 attack campaign
("Mini Shai-Hulud" npm worm) and a real Claude Code CVE (CVE-2026-25725),
both of which use exactly this SessionStart-hook persistence mechanism.
Ports the existing TS scanner's logic (hook-handler.cjs has no build step)
into scanSessionStartSettingsForRisk(), wired into the 'session-restore'
command handler in both the root and package copies of hook-handler.cjs
(kept byte-identical per the existing parity test). An adversarial-critic
subagent review found and this session fixed one real issue pre-commit: a
non-SyntaxError JSON.parse failure would have aborted session-restore
itself rather than degrading gracefully, since this path runs
unconditionally on every boot (unlike the opt-in MCP-tool path #3835
hardened the same way).
Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01HW4yuGGcrCy4CcPgQ7Q2dx