Agent Notes (updated 2025-11-13)
- TUIs at a glance:
timestamp_textual_app.py(TimestampLogApp): capture constraints and viewartifact.md(Markdown). It doesn’t run NLCO.agent_manual_pkg/src/agent_manual_pkg/tui.py(Agent Manual TUI): interactive agent runner (satisfaction, memory, DSPy logs).- Legacy
nlco_textual.pywas removed; use the two TUIs above instead.
- Headless alternative is
nlco_iter.py(console loop). Run:python nlco_iter.py. - You do NOT need both at once; the TUI runs iterations itself. Avoid concurrent runs (shared files).
- Run commands:
- Timestamp (wrapper, recommended):
./timestamp_tui.sh - Timestamp (one-liner, hardened):
stty iutf8; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 ./timestamp_textual_app.py --lenient-input --fallback-encoding cp1252 - Agent Manual:
python -m agent_manual_pkg.cli(supports--modeland--max-tokens).
- Timestamp (wrapper, recommended):
- Files touched: none (informational change only).
Things to keep in mind
- Textual and Rich must be installed to run the Timestamp TUI.
- MLflow is optional for headless; structured schedule JSON is no longer produced by the refiner.
- Timestamp TUI default paths now live under the user’s private directory:
- Default base:
~/.nlco/private(override withNLCO_PRIVATE_DIR). - Resolved files:
constraints.md,artifact.mdin that directory. - Override per-file via env:
TIMESTAMP_CONSTRAINTS_PATH,TIMESTAMP_ARTIFACT_PATHor CLI flags--constraints-path/--artifact-path.
- Default base:
timestamp_textual_app.pyappends to the resolved constraints file and can be used alongside NLCO tools, but beware of concurrent writes to the same file.
Path migration note (2025-11-13)
- We did NOT move any existing
constraints.md/artifact.md. They remain at repo root, untracked. - To keep using them without moving:
TIMESTAMP_CONSTRAINTS_PATH="$PWD/constraints.md" TIMESTAMP_ARTIFACT_PATH="$PWD/artifact.md" ./timestamp_textual_app.py. - To migrate to the new defaults (
~/.nlco/private):- One‑liner (hardened):
IFS=$'\n\t'; set -euo pipefail; umask 077; base="${NLCO_PRIVATE_DIR:-$HOME/.nlco/private}"; install -d -m 700 "$base"; for f in constraints.md artifact.md; do src="$PWD/$f"; dst="$base/$f"; if [ -f "$src" ] && [ ! -e "$dst" ]; then mv -- "$src" "$dst" && echo "moved $f -> $dst"; else echo "skipped $f"; fi; done
- One‑liner (hardened):
Path migration executed (2025-11-13)
- On this machine, we moved
artifact.md→~/.nlco/private/artifact.mdand found~/.nlco/private/constraints.mdalready present; repo‑rootconstraints.mdwas untouched. - TUI now reads/writes from the private paths by default; override via
TIMESTAMP_CONSTRAINTS_PATH/TIMESTAMP_ARTIFACT_PATHif needed. - Context now includes weekday explicitly:
Datetime: YYYY-MM-DD HH:MM:SS (Friday)for better temporal grounding. - Auto-backups: Before any write to
constraints.md, we snapshot the current file to.nlco/backups/{hourly|daily|weekly}/constraints-*.mdif the period’s file doesn’t exist yet. Env override:NLCO_BACKUP_DIR. - Constraints tail sizing: In
timestamp_app_core, tail now always derives from pane height (tail = max(height - 2, 1)). The oldTIMESTAMP_CONSTRAINTS_TAILnumeric env is ignored for rendering. - Mobile SSH tip: some clients clip the last column. Use either
--right-margin N(envTIMESTAMP_RIGHT_MARGIN) or--pad-eol(envTIMESTAMP_PAD_EOL=1) to add space at the right; both affect rendering only (no file changes).
Key bindings (Timestamp TUI)
gifocus input •gafocus artifact •F1toggle help (more reliable thanCtrl+Hon some phones/SSH) •Ctrl+Cexit •PageUp/PageDownscroll artifact.
Next Steps (2025-11-13)
- 46a. Wire
TimewarriorModule.run()intonlco_iter.iteration_loop()behind envNLCO_TIMEW=1; log a short status line and add 2 unit tests (timew present/absent). Recommended. - 46b. Add a minimal “unchanged twice” stop rule to headless iterations to prevent endless runs; 2 tests (no-change stops, change resets counter).
- 46c. Apply
TIMESTAMP_RIGHT_MARGINpadding intimestamp_app_core.TimestampLogApp(Constraints Markdown) and add one style assertion test. (Tracking.) - 46d. Prune remaining legacy references to
nlco_textual.pyin docs and code comments; keep the file but mark clearly as deprecated. - 46e. Harden JSONL model logging for path errors (permission/dir missing) with a tiny try/except and one test; keep code minimal.
- 46f. (Done) Lightweight advisory file lock to avoid simultaneous writes to
constraints.mdwhen headless + timestamp/web app run together.- New:
file_lock.locked_file(path, mode='a+')(fcntl LOCK_EX; Linux only). - Used by:
constraints_io.append_line,timestamp_textual_app._append_to_constraints,webapp/nlco_htmx/utils.write_constraints_entry. - Test:
tests/test_constraints_locking_utils.pyspawns two processes appending concurrently; asserts one heading and all lines present.
- New:
Proposed Next Steps (80)
- 80a. Split
TUI.apply_memory_updatesinto*_create/_update/_deletehelpers with one focused unit test. Minimal code; lowers CC hotspot. (Recommended.) - 80b. Style cleanup in
tui.py: remove semicolons flagged by ruff (E702/E703); no behavior change; quick win + keep lint clean. - 80c. Wire
TimewarriorModule.runintonlco_iterbehindNLCO_TIMEW=1with 2 tests (timew present/absent). Small, controlled change toward earlier goals. - 80d. Add a tiny
/helpcommand that echoes HelpScreen text into#log; add one assertion in tests to lock UX.
Shell hardening cheats
- One-liner (ad‑hoc):
stty iutf8; LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 ./timestamp_textual_app.py --lenient-input --fallback-encoding cp1252 - Persistent (bash/zsh): add
[ -t 0 ] && stty iutf8 || trueto your shell rc.
Models & budgets (NLCO iter)
- Primary LM:
deepseek/deepseek-reasonerwithmax_tokens=40000(seenlco_iter.py). - Support LM for subsystems:
deepseek/deepseek-chatwithmax_tokens=4000,temperature=0(used in various support modules). - Memory now uses the primary LM (reasoner) in headless.
Memory module limits
MemoryModule.max_iters= 4 by default. Each ReAct step can call a tool (e.g.,replace_memoryorappend_memory).- One
replace_memorycall replaces all occurrences of the search string inmemory.md(not just one), and increments the edit counter. - Effective bound per invocation: up to 4 tool-driven edits, but fewer if the ReAct loop decides to stop early.
- No explicit OpenRouter reasoning budget is set; if routed via OpenRouter, provider defaults apply. We do not pass
reasoning/max_reasoning_tokenstoday.
Reasoning trace display
- nlco_iter prints a “Model Reasoning · Refiner” panel when the provider returns native reasoning. Critic is currently disabled.
- DeepSeek API: reads
message.reasoning_content. - OpenRouter: reads
message.reasoning.{text|summary}.
- DeepSeek API: reads
- To force reasoning over OpenRouter, pass the unified
reasoningparameter (e.g.,{"enabled": true}or{"effort": "medium"}) via your LM config; we kept runtime code minimal and non-intrusive.
Model output logging
- All model outputs (including reasoning if present) append to JSONL at
.nlco/model_log.jsonl. - Set
NLCO_MODEL_LOG=/path/to/file.jsonlto change the destination. - Each line:
{ ts, stage, output, reasoning }.
Textual Markdown
- Textual provides a
Markdownwidget that parses Markdown with a GFM-like parser (tables, task lists, strikethrough, autolinks). - For interactive/spreadsheet-like tables, use
DataTable; Markdown tables are static. - TimestampLogApp now renders
artifact.mdviaMarkdown(read-only) instead of aTextArea.
Timestamp TUI complexity reductions (2025-11-12)
- Merged CLI/TTY preflight across modules:
timestamp_textual_app.pynow delegates totimestamp_app_corefor CLI parsing and UTF‑8 TTY checks, with a tiny wrapper that preserves thestty iutf8behavior required by tests. - Extracted
_build_appendto assemble constraint writes; keeps_append_to_constraintslinear and short. - Kept richer lenient‑input helper in wrapper (prints one warning, patches
read) since tests assert those messages; core retains a minimal variant. - Result: wrapper no longer has any CC ≥ C; worst offenders are B(10/8/6). Core remains B‑level for
_ensure_utf8_ttyand_parse_cli. - Constraints pane: factored wrapper’s constraints logic into helpers —
_md_preserve_lines,_maybe_scroll_constraints_end. The old_tail_envhelper was removed; the wrapper now derives the tail from the pane height (tail = max(height − 2, 1)), matching the core._load_constraintsstays straight‑line and ≤ B. Tests updated accordingly.- Added tests:
tests/test_timestamp_constraints_helpers.pycovers_tail_envdefaults/values/invalids, newline preservation, and autoscroll focus/no-focus behavior.
- Added tests:
Legacy: Old Vim Input
_OldVimInput(previously intimestamp_textual_app.py) has been removed. The app usesVimInputfromtimestamp_vim_input.py.
Release
-
v0.1.1 (2025-11-06): reasoning trace panels in
nlco_iter, JSONL model logging, TimestampLogApp Markdown view +giinput focus, tests updated. -
v0.1.2 (2025-11-10): TimestampLogApp adds a minimal UTF-8 TTY preflight to avoid Textual
UnicodeDecodeErroron misconfigured terminals; new tests intests/test_timestamp_textual_preflight.py. -
v0.1.3 (2025-11-11): Add a 1‑column right padding to the
Logwidget intimestamp_textual_app.pyto avoid last‑column clipping observed on some mobile SSH clients (e.g., JuiceSSH/Termux). New testtests/test_timestamp_textual_layout_margin.pypins this CSS. -
v0.1.4 (2025-11-11): Optional lenient input hook for Textual: set
TIMESTAMP_LENIENT_INPUT=1to monkeypatchtextual.drivers.linux_driver.decodeto fall back tocp1252(orTEXTUAL_FALLBACK_ENCODING) when non‑UTF‑8 bytes arrive. Tests:tests/test_timestamp_textual_lenient_input.py. -
v0.1.5 (2025-11-11): Fix script entry NameError by defining the lenient hook before
main()/__main__guard; addtests/test_timestamp_textual_entry_order.pyto ensure running the script directly doesn’t raise NameError and exits cleanly. -
v0.1.6 (2025-11-11): Add tests: constraints append behavior (
tests/test_timestamp_constraints_append.py), lenient warn-once (tests/test_timestamp_textual_lenient_warn_once.py), preflight success path (tests/test_timestamp_textual_preflight_success.py), and timestamp formatting (tests/test_timestamp_format_line.py). -
v0.1.7 (2025-11-11): Add CLI flags to TimestampLogApp script:
--lenient-inputto enable decode fallback and--fallback-encoding ENCto select the fallback codec (defaultcp1252). Testtests/test_timestamp_textual_cli_lenient.pyverifies the flag path without launching a real UI. -
v0.1.8 (2025-11-11): Strengthen lenient input: in addition to patching
linux_driver.decode, also patchlinux_driver.readto sanitize non‑UTF‑8 bytes (fallback → UTF‑8) before they reach the decoder. This addresses cases where the driver binds the originaldecodeat definition time. -
v0.1.9 (2025-11-11): Add
timestamp_tui.shwrapper which sets UTF‑8 locale, enablesiutf8, and runs the app with--lenient-input --fallback-encoding cp1252. Testtests/test_timestamp_shell_wrapper.pyasserts wrapper contents. -
v0.1.10 (2025-11-11): Ensure
timestamp_tui.shhas executable bit set in repo workspace. -
v0.1.11 (2025-11-11): Document copy‑paste one‑liner and step‑by‑step shell hardening commands.
-
v0.1.11 (2025-11-11): Document quick shell hardening commands (UTF‑8 locale +
iutf8) for ad‑hoc sessions. -
v0.1.12 (2025-11-11): Add
--right-margin Nto adjust Log right padding at runtime (envTIMESTAMP_RIGHT_MARGIN). Helps phones/SSH clients that clip the last column. Test:tests/test_timestamp_cli_right_margin.py. -
v0.1.13 (2025-11-11): Add
--pad-eol(envTIMESTAMP_PAD_EOL=1) to append a single space when rendering each log line, without writing it to file—works around last-column clipping that margins don’t solve. Test:tests/test_timestamp_cli_pad_eol.py. -
v0.1.14 (2025-11-11): Make help reliable on mobile/SSH by adding
F1binding fortoggle_help(some clients translateCtrl+Hto backspace). Added tests:tests/test_timestamp_help_binding_and_action.py. -
v0.1.15 (2025-11-11): Make artifact Markdown view scrollable via CSS
overflow: auto, allow focusing it (gashortcut) and mark focusable. Tests:tests/test_timestamp_artifact_scrollable.py,tests/test_timestamp_artifact_focus_shortcut.py. -
v0.1.16 (2025-11-11): Replace the upper Log with a scrollable Constraints Markdown pane. It renders
constraints.mddirectly and auto-refreshes. Shortcutgifocuses input,gafocuses artifact. Tests:tests/test_timestamp_constraints_view_load.py; updated CSS test to reference#constraints-viewpadding. -
v0.1.17 (2025-11-11): Default the Constraints pane to show the bottom (latest entries). On each load/refresh, we call
scroll_end(); if supported,auto_scroll=Trueis set. Test updated intests/test_timestamp_constraints_view_load.pyto assert scrolling. -
v0.1.18 (2025-11-11): Make auto-scroll polite: it’s disabled while the constraints pane is focused, and can be turned off with
--no-auto-scroll(envTIMESTAMP_AUTO_SCROLL=0). Tests:tests/test_timestamp_no_auto_scroll_flag.py,tests/test_timestamp_constraints_focus_blocks_autoscroll.py. -
v0.1.19 (2025-11-11): More tests for constraints pane: mtime-driven refresh (
tests/test_timestamp_constraints_refresh_mtime.py), missing-file handling (tests/test_timestamp_constraints_missing_file.py), andgiinput-focus shortcut (tests/test_timestamp_input_focus_shortcut.py). -
v0.1.20 (2025-11-11): Remove structured schedule output from the Refiner.
RefineSignaturenow returns onlyrefined_artifact; headless and TUI paths no longer write or parsestructured_schedule.json. Tests updated accordingly. -
v0.1.21 (2025-11-11): Remove Critic module and input from Refiner.
RefineSignaturedrops thecritiquefield; headless and Textual flows no longer call or display Critic. TUI “Critique” panel removed. Tests updated. -
v0.1.22 (2025-11-11): Add
SystemStatePydantic model withlast_artifact_update(ISO). Passed to the Refiner right afterconstraintsin both headless and Textual flows. Tests:tests/test_system_state_refiner_input_headless.py,tests/test_system_state_refiner_input_textual.py. -
v0.1.23 (2025-11-11): (historical)
nlco_textual.pywas made executable. This TUI is now legacy and not maintained. -
v0.1.24 (2025-11-11): Repo housekeeping — commit and push TUI + pipeline changes (mobile SSH fixes, scrollable panes, constraints view overhaul, removal of Critic/structured schedule, new SystemState input) and tests.
-
v0.1.25 (2025-11-11): (historical)
nlco_textual.pyremoval was planned. The file may still be present but should be treated as deprecated; use headless loop (python nlco_iter.py) ortimestamp_textual_app.py. -
v0.1.26 (2025-11-11): TimestampLogApp now tails
constraints.mdby default (last 200 lines) and scrolls to bottom. Flags/env:--constraints-tail N(envTIMESTAMP_CONSTRAINTS_TAIL) to adjust;--no-auto-scrollto stop snapping to end. -
v0.1.27 (2025-11-11): Tests for artifact scroll actions and fallbacks (
tests/test_timestamp_artifact_scroll_actions.py). -
v0.1.28 (2025-11-11): TimestampLogApp respects newlines in the constraints view by emitting Markdown line breaks. Test:
tests/test_timestamp_constraints_newlines.py. -
v0.1.29 (2025-11-12): Remove unused
_OldVimInputfromtimestamp_textual_app.py; the TUI relies solely onVimInput. -
v0.1.30 (2025-11-12): Add hourly/daily/weekly auto-backups for
constraints.md(locked writes). New modulebackups.py; used byconstraints_io,timestamp_textual_app, and HTMX utils. Tests:tests/test_constraints_backups.py. -
v0.1.31 (2025-11-12): Constraints tail now always tracks pane height in
timestamp_app_core. Tests:tests/test_timestamp_constraints_tail_auto.pyand updated display/tail default tests. -
v0.1.32 (2025-11-12): Ran
ruff check .across repo; 397 findings, 166 auto-fixable. Consider adding a minimalpyproject.tomlRuff config and staged fixes. -
v0.1.33 (2025-11-12): Applied safe Ruff auto-fixes (
ruff check . --fix). Findings reduced to 221 from 397; remaining include E402/E70x/F841 and a few F821/E722. No code semantics changes intended. -
v0.1.34 (2025-11-12): Added minimal Ruff config
.ruff.toml(py311, line-length 100, ignore E501; per-file ignores for legacy/intentional patterns; excluded one experimental file). Current findings with config: 102. -
v0.1.35 (2025-11-12): Fixed high-signal Ruff issues: F821 in
agent_manual_b.py,interactive_chat.py,textual_dspy/app.py; E722 inabbrev_decoder/...andonline_optimization_system.py; minor F841 cleanups. Added targeted per-file ignores for tests, world_model, and submodules.ruff checknow passes clean with.ruff.toml. -
v0.1.46 (2025-11-17): Headless path unification + zsh helper + tests.
- nlco_iter now resolves artifact/constraints/memory paths via
timestamp_app_core.resolve_*, defaulting to~/.nlco/private/(keeps files out of the repo). Env overrides still work:NLCO_{CONSTRAINTS,ARTIFACT,MEMORY,SHORT_TERM}_PATHorTIMESTAMP_*_PATH. - Added minimal CLI
scripts/constraints_add_entry.pyto append a single constraints line withHH:MM:SStime and daily headings. Flag--nowexists only for tests. - Added
scripts/zsh_functions.zshexposinga(); usage:a your messageappends to constraints using the same helpers/paths as the TUI/HTMX. - Tests:
tests/test_add_constraint_script.pyensures day heading and line formatting, deduping headings on same day, and new day headings. - Artifact view defaults to top scroll already; tests in place:
tests/test_timestamp_artifact_default_top.pyandtests/test_timestamp_artifact_no_autoscroll.py(envTIMESTAMP_AUTO_SCROLL=0disables it).
- nlco_iter now resolves artifact/constraints/memory paths via
-
v0.1.47 (2025-11-17): Quick usage cheat‑sheet for constraints appends.
- Zsh function: source
scripts/zsh_functions.zsh:1then runa do the thing. - CLI:
python3 scripts/constraints_add_entry.py did the thing(uses HH:MM:SS; daily# YYYY-MM-DD). - Path resolution: honors
NLCO_CONSTRAINTS_PATH→ default~/.nlco/private/constraints.md. - Verification:
tail -n 5 ~/.nlco/private/constraints.md(or the env‑overridden path).
- Zsh function: source
Notes/Learnings
- Shell stays minimal by delegating to the tested Python helper; consistent behavior with TUI/HTMX.
- Memory replace tool prints unified diffs; look for them in nlco_iter console output.
File locations (default; outside repo)
- Constraints:
~/.nlco/private/constraints.md(setNLCO_CONSTRAINTS_PATHto override) - Artifact:
~/.nlco/private/artifact.md(setNLCO_ARTIFACT_PATH) - Memory:
~/.nlco/private/memory.md(setNLCO_MEMORY_PATH) - Short-term:
~/.nlco/private/short_term_memory.md(setNLCO_SHORT_TERM_PATH)
Quick zsh function (a)
- Source once in your shell rc:
source /home/tom/git/agent/scripts/zsh_functions.zsh - Use:
a did a quick thing→ writesHH:MM:SS did a quick thingunder today’s# YYYY-MM-DDheading.
What we learned / keep in mind
-
Sharing the path resolvers across TUI and headless keeps artifacts out of the repo by default and avoids FileNotFound races.
-
Keep shell glue minimal; delegate formatting and locking to Python helpers we already test.
-
v0.1.36 (2025-11-12): Simplify Timestamp TUI constraints pane — wrapper tail derives from visible pane height; env
TIMESTAMP_CONSTRAINTS_TAILis ignored for rendering. Removed_tail_env. Tests updated:tests/test_timestamp_constraints_tail_view.py,tests/test_timestamp_constraints_helpers.py, andtests/test_timestamp_constraints_newlines.pyadjusted to inject pane height. -
v0.1.37 (2025-11-12): Delegate wrapper helpers to core: added tiny shared helpers in
timestamp_app_core.py(md_preserve_lines,constraints_tail_from_height,scroll_end) and made the wrapper call them. Addedtests/test_timestamp_constraints_equivalence.pyto assert wrapper/core render identical content for the same pane height. -
v0.1.38 (2025-11-12): Wrapper now respects
TIMESTAMP_CONSTRAINTS_ROWSlike the core. On mount, it sets#constraints-containerheight and uses it to derive tail (rows-2). Addedtests/test_timestamp_constraints_rows_env_wrapper.pyto validate height + tail. -
v0.1.39 (2025-11-12): Unified TUI apps:
timestamp_textual_app.TimestampLogAppnow subclasses the coretimestamp_app_core.TimestampLogAppto remove duplicate CSS/compose and reuse helpers. Wrapper overrides only: key bindings, timers/refresh, input submit formatting, help toggle, artifact scroll actions, and a focus‑aware_scroll_constraints_end(skips autoscroll when constraints pane focused). Tests added:tests/test_timestamp_constraints_equivalence.py(wrapper vs core rendering),tests/test_timestamp_artifact_scroll_helpers_delegate.py(wrapper delegates to core scroll helpers). -
v0.1.40 (2025-11-12): Extract constraints append formatting to shared helper
constraints_io.build_append_block(existing, needs_heading, date_str, line). Wrapper now uses it in_append_to_constraints. New tests:tests/test_constraints_append_helper.pycovers first-entry, same-day, and next-day w/o trailing newline. -
v0.1.41 (2025-11-12): Add smoke tests using Textual
run_test()for both core and wrapper TimestampLogApp to ensure they compose and render without launching a real UI (tests/test_timestamp_smoke_run_test.py). -
v0.1.42 (2025-11-12): Wrapper CLI adds
--constraints-rows Nto setTIMESTAMP_CONSTRAINTS_ROWS(mirrors core env). Test:tests/test_timestamp_cli_constraints_rows.py. -
v0.1.43 (2025-11-12): HTMX writer now uses shared
build_append_blockfor consistent constraints formatting. Updatedwebapp/nlco_htmx/utils.write_constraints_entry. New testtests/test_web_htmx_append_block_consistency.pycovers next-day insert with missing trailing newline. -
v0.1.44 (2025-11-12): Change timestamp format from
HHMMtoHH:MM:SSacross TUI and HTMX. Updated_format_lineintimestamp_textual_app.pyandwrite_constraints_entryinwebapp/nlco_htmx/utils.py. Tests updated accordingly (Textual app formatting, HTMX POST/API, and consistency test). -
v0.1.45 (2025-11-12): Artifact pane now scrolls to the top by default (polite
auto_scrollgating). Core callsscroll_homeafter load; wrapper inherits behavior. Added tests:tests/test_timestamp_artifact_default_top.pyfor core and wrapper.
Things learned / to keep in mind (2025-11-12)
- Two
TimestampLogAppclasses exist (core and wrapper). Their behaviors can drift; we aligned tailing behavior to reduce divergence. Consider consolidating or delegating constraints logic from the wrapper to the core in a future change. - CLI still accepts
--constraints-tailand setsTIMESTAMP_CONSTRAINTS_TAILfor backward compatibility, but the wrapper ignores it during rendering. Tests only assert env propagation. - Textual's
Markdown.updatemay emit a benign "no running event loop" message when called on stubbed views in tests; currently harmless and can be ignored. - Helper naming: the core calls
_scroll_constraints_end. For backward‑compat with older tests, the wrapper keeps_maybe_scroll_constraints_end()and forwards it to_scroll_constraints_end(). - Append logic centralization: both headless and TUI paths should use
constraints_io.build_append_blockfor consistent spacing/heading behavior; today only the wrapper calls it. Consider adopting it in any other path that writes constraints to avoid drift.
Structured Memory — Options (2025-11-11)
- Option A (light): add sectioned headings in
memory.md(Policies/Procedures/Glossary) and constrain tools to edit within a selected section; add tests for section targeting. - Option B (tags): require a tiny YAML front‑matter per block (
tags: [policy, time],updated:). Provide a minimalappend_memory --tagshelper and validate via tests. - Option C (index JSON): maintain
.nlco/memory_index.jsonwith{id, title, tags, updated, offset}for quick lookup; unit‑test index build and lookup. - Option D (RAG-lite): embed memory blocks once (e.g., 384‑d float per block) and select top‑k to show to the LM; start with a toy cosine impl + tests on selection only.
- Option E (recency window): inject only blocks updated in last N days into
contextwhen memory changed; add a test that verifies injection happens only on recent edits. - Option F (write rules): add a 2‑rule acceptance gate: “non‑transient + reusable”; if not satisfied, do not write. Test with examples.
- Option G (CLI): tiny
./mem.py list|show|appendto manipulate memory deterministically; add smoke tests for the commands.
Quick Run — Textual Apps (cheat sheet)
- Install deps once:
source .venv/bin/activate || true; pip install -r requirements.txt - Legacy NLCO TUI: deprecated and not maintained; examples removed. Use headless
nlco_iter.pyortimestamp_textual_app.py. - Timestamp TUI (notes/constraints):
./timestamp_tui.sh(recommended)- Alt:
./timestamp_textual_app.py --lenient-input --fallback-encoding cp1252 - Phone/SSH hardening:
stty iutf8 && export LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8
- Alt:
TTY / UTF-8 preflight (TimestampLogApp)
- Symptom:
UnicodeDecodeErrorfromtextual.drivers.linux_driveron launch when the terminal isn’t UTF-8 orstty iutf8isn’t set. - Change:
timestamp_textual_app.pynow checks for (a) TTY stdin/stdout, (b) UTF-8 locale, and (c)stty iutf8. If any fail, it exits with a clear message rather than crashing inside Textual. - Additionally: a small
main()wrapper prints a concise hint and exits if aUnicodeDecodeErrorescapes the app, though Textual may still render its own traceback from a background thread. The fix remains to correct the terminal environment. - How to fix env: ensure a UTF-8 locale (e.g.,
export LANG=en_US.UTF-8) and enable UTF-8 input on the TTY:stty iutf8. - Tests:
tests/test_timestamp_textual_preflight.pyexercises the three failure modes with monkeypatching.
Troubleshooting: iutf8 disabled
- Enable for current shell:
stty iutf8(run in the same pane you launch the app from). - Verify:
stty -a | grep -E -- '-?iutf8'→ should showiutf8(not-iutf8). - Persist for interactive shells (bash/zsh): add to
~/.bashrcor~/.zshrc:[ -t 0 ] && stty iutf8 || true
- tmux/screen: run
stty iutf8inside each pane; to persist, keep the shell-rc line above (it runs only in interactive TTYs).
Right edge clipping (mobile SSH)
- Symptom: rightmost character of many lines is missing when running TimestampLogApp over JuiceSSH/Termux.
- Likely cause: terminal last-column/autowrap quirk or off‑by‑one width reporting over SSH. Textual/Rich will happily write into the last cell; some terminals fail to render it.
- Mitigation (2025-11-11): increased right padding on the
Logwidget (padding: 1 2;) so content stays one cell away from the terminal’s right edge. - Quick check on client: compare
tput colsvsstty -a | grep -o 'columns [0-9]\+'; they should match. EnsureTERM=xterm-256colorand locale is UTF‑8. Minimal probe:printf '%*sX\n' "$COLUMNS" ''should visibly print anXin the last column.
Non‑UTF‑8 input over SSH (2025-11-11)
- Symptom: Textual prints a background thread traceback with
UnicodeDecodeError: invalid start byte 0x..fromtextual.drivers.linux_driver.decode. - Cause: the SSH client sends bytes that aren’t UTF‑8 (often CP1252 like 0x99 for ™).
iutf8doesn’t transcode; it just changes line editing. Textual expects UTF‑8 and crashes. - Fix on the remote shell:
export LANG=en_US.UTF-8; export LC_ALL=en_US.UTF-8; stty iutf8(run inside tmux panes too). Verify withlocale charmap→UTF-8andstty -ashowsiutf8. - Fix on the client (examples): set JuiceSSH/Termux character encoding to UTF‑8 and disable any legacy encoding. Avoid pasting content that yields CP1252 bytes; the hex for ™ should be
e2 84 a2(UTF‑8), not99. - Probe: run
xxd -pthen paste a ™ and press Enter; if you see99, your client isn’t sending UTF‑8. - App behavior: we warn pre‑launch, but Textual may still show its own traceback if non‑UTF‑8 bytes arrive later. We prefer environment fixes over code fallbacks to keep the app minimal.
- Opt‑in fallback: set
TIMESTAMP_LENIENT_INPUT=1(and optionallyTEXTUAL_FALLBACK_ENCODING=cp1252) to enable a small monkeypatch that decodes bad bytes via cp1252. This is intentionally off by default to avoid hiding real issues. - CLI alternative: run
./timestamp_textual_app.py --lenient-input [--fallback-encoding cp1252]to toggle without env vars. Flags are parsed minimally and ignore unknown args. Quick run (lenient input) - One line:
./timestamp_textual_app.py --lenient-input --fallback-encoding cp1252 - With env hardening for this shell:
export LANG=en_US.UTF-8(or keepC.UTF-8)export LC_ALL=en_US.UTF-8stty iutf8./timestamp_textual_app.py --lenient-input --fallback-encoding cp1252
Shell wrapper (shortest path)
- Run:
./timestamp_tui.sh - It sets
LANG/LC_ALL, runsstty iutf8if on a TTY, then executes the TUI with lenient input.
Quick shell hardening (no run)
- Per‑pane:
stty iutf8 - Locale:
export LANG=en_US.UTF-8andexport LC_ALL=en_US.UTF-8 - Optional:
export TERM=xterm-256color
Copy‑paste one‑liner (only harden shell)
stty iutf8 && export LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 TERM=xterm-256color
Repo housekeeping (2025-11-10)
- Committed and pushed v0.1.2 changes: UTF-8 TTY preflight in
timestamp_textual_app.py, error hint inmain(), and teststest_timestamp_textual_preflight.py+test_timestamp_textual_main.py.
Repo housekeeping (2025-11-13)
- README refocused on NLCO headless loop and Timestamp TUI. DeepSeek batch docs moved to a short subpackages note. Added hardened one‑liner and wrapper as primary run paths.
- README polished for readability: compact contents list, consistent section headers, and concise quick‑start. No behavior changes.
- README now features centered heading and Material‑style badges (shields.io); purely visual.
- Pushed documentation commits to origin/main.
Nootropics log (read-only)
- NLCO Textual UI now shows the last 72h of entries from
~/.nootropics_log.jsonlin a side panel. - Strictly read-only: the loader never writes or truncates the file.
- Env var
NLCO_NOOTROPICS_LOGcan point to a different JSONL file for testing. - Minimal schema: each line must be JSON with an ISO
tsfield; lines withouttsare skipped. - Helper:
append_nootropics_section(context)now appends the section; both headless and TUI call this instead of duplicating string glue.
Caching placement
- To maximize DSPy cache reuse, nootropics data is appended to the
contextinput (notconstraints). - This keeps the
constraintsstring stable across runs and pushes variable nootropics lines "behind" constraints in the prompt ordering. nlco_iter.pyappends aNootropics (last 72h)section at the end ofcontext.
NLCO iter tests
- Added tests covering headless iteration behavior without touching real LMs:
tests/test_nlco_iter_logging_and_schedule.pyensures artifact update, structured schedule JSON write, and JSONL model logs with reasoning.tests/test_nlco_iter_nootropics_context.pyasserts nootropics appear only incontextand thatconstraintsremain unchanged.- Existing
tests/test_nlco_iter.pyvalidates async memory invocation and artifact write.
- Note: Running the entire repo test suite may fail due to duplicate test module names in
packages/deepseek-batch/tests/. Run selective tests for NLCO iter.
Quick usage
- Headless (nlco_iter): scheduler decides when to run; finished-check is currently disabled.
- Timestamp app:
gifocuses the input if focus is elsewhere; use Enter to append a timestamped line.
Iteration counts
- After a constraints.md change (headless), the loop runs up to
MAX_ITERATIONSin one invocation. Override withNLCO_MAX_ITERS(default 3). - On scheduled hourly ticks (no changes), it runs exactly 1 iteration per tick.
- In the TUI, each press of
rruns exactly 1 iteration.
Layout tweak (2025-11-07)
- Removed NLCO TUI layout. Timestamp app has its own constraints height (8) documented below.
Timestamp app constraints
- TimestampLogApp now uses a fixed constraints height of 8 (
#constraints-container { height: 8; }). - Uses the shared
constraints_io.tail_linesfor tailing and scrolls to bottom by default. - Test:
tests/test_timestamp_constraints_height.pypins the height. - Tests:
tests/test_timestamp_constraints_display.pyverifies the tail content and scroll-to-end behavior;tests/test_timestamp_constraints_tail_default.pyensures default tail (200) doesn't trim small files. - Wrapper:
tests/test_timestamp_wrapper_exports.pysmoke tests thattimestamp_textual_appexposes the app and helpers, and thatmain()wires parse/tty/lenient/run calls without launching a real UI. - Env:
tests/test_timestamp_constraints_autoscroll_env.pyassertsTIMESTAMP_AUTO_SCROLL=0disables scrolling to end on reload.
Refactor: Timestamp app split
timestamp_vim_input.pycontains the minimalVimInputwidget.timestamp_app_core.pyholds the app class and helper functions.timestamp_textual_app.pyis a thin wrapper that re-exports the app, helpers, and main().
Constraints pane behavior (Timestamp app)
- Tail count = max(pane height − 2, 1). No implicit fallback.
- Env
TIMESTAMP_CONSTRAINTS_ROWS=Nsets the container height toNrows at mount time and drives tail count (N-2). - Scrolls to the end after refresh so the bottom is visible by default.
File-first constraints behavior
- Shared helpers
constraints_io.tail_linesandconstraints_io.append_linecentralize file tailing and appending.
Short‑term memory
- File:
short_term_memory.md. - Producers:
TimewarriorModule: appends a one‑line event whenever it starts/stops or prints a notable status.ExecutiveModule: appends a one‑line trace per tool action when used (not currently wired in headless; TUI doesn’t invoke ExecutiveModule).
- Consumers: none at runtime; we don’t read it back into model context today. It’s for lightweight, append‑only breadcrumbs.
Potential rough edges observed
nlco_iter.pydisables finished-check (if False:), so it may iterate indefinitely unless externally stopped.- Legacy
nlco_textual.pyalso writes files in place; avoid running it alongside headless to prevent clobbering. - Timewarrior context: headless
nlco_iter.pycurrently does not callTimewarriorModule.run, so no Timewarrior info is added to the model context. In the Textual UI,TimewarriorModule.runis invoked and its output is shown in the "Timewarrior" pane, but it is not injected into thecontextstring passed to Critic/Refiner. - Critic stage disabled (2025-11-08): We skip the Critic call and pass an empty critique to Refiner. The TUI shows “Critic disabled” in the Critique panel.
- Planner stage: In the Textual UI we call
PlanningModule.runand show its output; in headlessnlco_iter.pythe planner is instantiated but not called yet.
Future test ideas
- Add a Textual
App.run_test()smoke test to ensureNLCOTextualAppcomposes and updates logs without launching a real UI. - Validate that running one iteration updates the artifact. Structured schedule JSON is no longer produced by default.
- Model lineage update (2025-09-29)
- DeepSeek docs state both
deepseek-chatanddeepseek-reasonerwere upgraded toDeepSeek-V3.2-Exp;chat= non-thinking mode,reasoner= thinking mode. Over OpenRouter, reasoning appears in thereasoningfield; via DeepSeek API it appears asmessage.reasoning_content. Timewarrior Control — Conceptual Plan
- DeepSeek docs state both
- Current state:
TimewarriorModuleis wired for use but not yet invoked in the headless loop (nlco_iter.py). We plan to gate it behindNLCO_TIMEW=1and log a concise status line each iteration. - Likely failure cause: headless flow never invokes Timewarrior; summary parsing is brittle (looks for a "Tags" line and the phrase "there is currently no active time tracking").
- Minimal enablement: call
timewarrior_tracker.run(...)early initeration_loop(before memory/planning) and log the result. Add two tests that monkeypatch the tool to simulatesummaryandstart/stopsuccess. - Robust detection: prefer
timew exportJSON (ortimew get dom.active*) to detect active state + tags instead of scrapingsummary. Keep it simple—no fallbacks unless asked. - Control policy: default to deterministic rules from
structured_schedule.json(start/stop on block boundaries; derive tags from the active block). Use the LLM reviewer only when schedule vs. context disagree. - Safety and UX: add a 2–3 minute hysteresis to avoid flapping; add a dry-run flag; allow manual overrides via constraints lines (e.g.,
timew:start tag1 tag2,timew:stop). - Observability: record
{ts, action, tags, justification, stdout/stderr}to.nlco/timew_log.jsonl; unit-test both “timew missing” and normal paths.
Artifact improvement (concept-only, 2025-11-08)
- Minimal acceptance gate: only replace the baseline artifact when a tiny rubric score improves. No fallbacks.
- Rubric inputs: constraints-derived checklist coverage, schedule consistency, and fewer TODO/TBD markers; return 0..100.
- Loop tweak: compute score(prev) and score(candidate); accept candidate if strictly higher (or equal with fewer TODOs). Keep a “best so far”.
- Focus cue: use
Affect.suggested_focusto generate a one-liner “focus for next iteration” and pass it to the refiner. - Tests proposed (not yet implemented): two-unchanged-hashes stop, accept-only-on-improvement, and context-frozen-per-iteration. Memory handling (summary, 2025-11-11)
- File
memory.mdis the persistent knowledge base; edits are made only when durable info should be kept. - Module
MemoryModuleruns a small ReAct loop with toolsshow_memory,replace_memory,append_memory,reset_memoryand writes back only if changes occurred. - Headless and Textual flows both use the primary LM for memory updates; a short result string is printed to the Memory pane when changes happen.
- We don’t inject
memory.mdback into the model context yet (display-only except for edits). Short-term breadcrumbs go toshort_term_memory.mdseparately. Code Quality Snapshot (2025-11-12) - Refreshed Radon (post-constraints refactor): repo avg CC C ≈ 14.89. Top hotspots unchanged; constraints pane no longer contributes.
- Radon CC (C–F) hotspots (function · score) — latest:
dspy_programs/memory_gan.main· D (24)dspy_programs/taskwarrior_agent.main· D (21)refiner_signature.render_schedule_timeline· C (20)agent_manual_pkg.tui.TUI._process_job· C (20)executive_module._execute_step· C (16)nlco_iter.iteration_loop· C (15)timewarrior_module._apply_decision· C (15)- Full listing captured via
radon cc -s -n C -a ..
- Radon MI: no grade‑C production files observed;
agent_manual_pkg/.../tui.pyremains MI “C” (legacy/test-heavy context). Seeradon mi -s ..
Actionable Quick Wins
timestamp_vim_input._handle_normal_mode_keyrefactored into helpers — now below C and no longer listed.nlco_iter.iteration_looprefactored internally (helpers for reading state, building context, logging, and refiner print) with no behavior change; CC dropped below C.- Agent TUI: unified command routing. Merged
_handle_inline_commandsinto a single_route_command, and kept a tiny_handle_commandwrapper for compatibility. Overlap eliminated for/model,/modules,/max_tokens, and/layout. - Tests for unified router: added
test_layout_command_via_on_inputand a full/modulesflow test to assert state transitions and configuration calls. The suite now covers/layout,/model,/modules, and/max_tokens(prompted and inline). Seeagent_manual_pkg/tests/test_tui_router_commands.pyand existing tests. - Split satisfaction update:
_process_jobnow calls_update_goals(prompt)and_update_score()separately; added unit tests for each. Kept_update_satisfactionas a tiny wrapper for compatibility. - Radon artifact (58d): added
scripts/gen_radon_report.pywhich writes JSON + minimal HTML to.nlco/meta/. Test:tests/test_radon_report.pymonkeys patches subprocess to avoid external dependency. timestamp_app_core._load_constraintssplit into small helpers (_tail_count,_constraints_text,_scroll_constraints_end) to simplify the constraints pane logic without behavior changes.- Extract subroutines from
nlco_iter.iteration_loop(context build, model calls, writeback) to lower CC without changing behavior. - In
timewarrior_module._apply_decision, add early returns for NONE/denied cases to flatten nesting. Radon Snapshot (2025-11-12, 01:35) - Repo average CC: C ≈ 14.89 (46 C–F blocks).
- Top hotspots unchanged:
agent_manual_pkg.tui.TUI.on_input_submittedD(26),dspy_programs/memory_gan.mainD(24),dspy_programs/taskwarrior_agent.mainD(21), plus several C(20–16) functions. - MI: no grade‑C production files;
agent_manual_pkg/.../tui.pyMI “C” (legacy/test heavy). Security scan (2025-11-13) - Scope: working tree, full Git history (patterns), and TruffleHog v2 entropy/regex pass.
- High‑risk secrets: none found (no private keys, AWS/GitHub/Slack tokens, Bearer tokens).
- .env files: only
.env.exampleis tracked (placeholders). Any.env*in the tree are untracked. - Emails: only test/example emails in content; commit metadata naturally contains author emails (not part of file contents).
- Nested repo note:
telegram-mcp-repo/.git/exists locally but is not tracked; avoid bundling this folder when archiving. - Largest blobs in history are code/log artifacts; no credential patterns detected in those blobs.
How to re‑run locally
- Quick grep (HEAD): `rg -n --hidden -P -g '!/.git' -g '!/.git/' -e '-----BEGIN (RSA |DSA |EC |OPENSSH )?PRIVATE KEY-----' -e 'ghp_[A-Za-z0-9]{36,}' -e 'github_pat_[A-Za-z0-9_]{80,}' -e 'AKIA[0-9A-Z]{16}' -e 'ASIA[0-9A-Z]{16}' -e 'xox[bap]-' -e 'hooks.slack.com/services/' -e 'Authorization: Bearer [A-Za-z0-9_-.]+'
- Full history (patterns):
git rev-list --all | while read c; do git grep -I -n --full-name -E '(AKIA[0-9A-Z]{16}|ASIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{36,}|github_pat_[A-Za-z0-9_]{80,}|xox[bap]-|hooks\\.slack\\.com/services/|BEGIN [A-Z ]+PRIVATE KEY|Authorization: Bearer )' "$c" || true; done - TruffleHog v2 (optional):
python -m pip install --user trufflehog==2.2.1 && trufflehog --regex --entropy=True --json --since_commit $(git rev-list --max-parents=0 HEAD | tail -n1) --branch $(git rev-parse --abbrev-ref HEAD) file://$PWD
Recommended hardening
- Add a
pre-commithook withgitleaksortrufflehog(kept minimal; fail on verified secrets only). - Enable GitHub secret scanning & push protection on the repo (if using GitHub).
- Convert
telegram-mcp-repoto a proper submodule or add packaging excludes so its local.git/never ships.
PII scrub (2025-11-13)
- Removed from Git history and remote:
constraints.md,memory.md,short_term_memory.md,notes.md,info.md. - Local copies preserved (untracked) and added to
.gitignore. - Safety: mirror backup at
/tmp/agent-pre-scrub-mirror-YYYYmmdd-HHMMSS, tagpre-scrub-20251113-060334, branchbackup/pre-scrub-20251113-060334. - Force-pushed rewritten history to
origin(all branches + tags). - If collaborators exist: they must
git fetch --all --pruneand hard reset their branches (history was rewritten).
Pre-commit secrets scan (2025-11-13)
- Hook:
.pre-commit-config.yamlwith a localsecrets-scanthat runsscripts/secrets_scan.shover staged files. - Patterns are high-confidence only (no entropy): private keys, GitHub tokens, GitHub PATs, AWS AKIA/ASIA, Slack webhooks, Bearer tokens.
- Setup once:
python -m pip install --user pre-commit && pre-commit install - Run ad-hoc:
bash scripts/secrets_scan.sh $(git diff --cached --name-only) - Tests:
tests/test_secrets_scan.pycovers clean and leaked cases.
PII double‑check (2025-11-13)
- Local repo: no occurrences of
constraints.md,memory.md,short_term_memory.md,notes.md,info.mdin any commit; high‑risk patterns only appear in documentation and the scan script (not secrets). - Remote
main: clean (no target files present). - Remote PR refs:
refs/pull/{1..17}/headstill contain the removed files (GitHub stores PR heads separately). Action: close these PRs and recreate from the newmain. GitHub will GC unreachable objects over time; to accelerate removal, contact GitHub Support. - Re‑run locally: see “How to re‑run” in Security scan; remote check:
git clone --mirror $REPO_URL /tmp/repo.git && cd /tmp/repo.git && <same scans>.
PII prevention policy (2025-11-13)
- Separation: personal logs/notes live outside the repo (default paths remain
constraints.md,memory.md,short_term_memory.md,notes.md,info.mdbut are .gitignored and treated as local state). - Blocking hooks: pre-commit
secrets-scanis mandatory for contributors (pre-commit install). A second hookforbid-pathsblocks staging any of the five Markdown files (matched by basename). - CI gate (planned): GitHub Actions job runs the same scans on PRs and fails if secrets or forbidden paths are touched.
- Repo settings: enable GitHub “Secret scanning” and “Push protection” in Settings → Code security and analysis.
- Packaging: add
.gitattributesexport-ignoreentries for those files to keep them out ofgit archiveand release tarballs (planned). - Incident playbook: if a leak occurs, run the scrub script (filter-repo), force-push with backups/tags, close PR refs, notify collaborators to hard reset.
Forbid-paths hook (2025-11-13)
- Hook:
.pre-commit-config.yamlforbid-pathsrunsscripts/forbid_paths.sh. - Deny-list:
constraints.md,memory.md,short_term_memory.md,notes.md,info.md(basename match). - Tests:
tests/test_forbid_paths.pyensures safe files pass and forbidden names fail.
Release (continued)
- v0.1.46 (2025-11-13): Temporary headless hotfix — added a local
artifact.mdto avoid a first-run crash while investigating path alignment. The file remained ignored by Git. - v0.1.47 (2025-11-13): Proper fix — headless now uses the shared resolver (
timestamp_app_core.resolve_artifact_path) soartifact.mdlives under~/.nlco/private(or env override). Removed the temporary repo file and added a tinyFileNotFoundErrorguard in_read_artifact_and_state()so first-run works without creating files up front. Tests:tests/test_nlco_iter_paths_env.py,tests/test_nlco_iter_missing_artifact.py. - v0.1.48 (2025-11-13): Align headless constraints path with TUI —
CONSTRAINTS_FILEnow usestimestamp_app_core.resolve_constraints_path()(defaults to~/.nlco/private/constraints.md). Updated testtests/test_nlco_iter_paths_env.pyto assert both artifact and constraints use shared resolvers. - v0.1.49 (2025-11-13): Add shared resolvers for memory files —
resolve_memory_path()andresolve_short_term_path()default to~/.nlco/private/{memory.md, short_term_memory.md}with env overrides. Headless now passes these toMemoryModuleandTimewarriorModule. Tests updated: extendedtests/test_timestamp_paths_env.pyandtests/test_nlco_iter_paths_env.py. - v0.1.50 (2025-11-13): Add tests asserting artifact does not auto-scroll when
TIMESTAMP_AUTO_SCROLL=0:tests/test_timestamp_artifact_no_autoscroll.py(core + wrapper). Complements default-top tests. - v0.1.51 (2025-11-13): Web app uses shared defaults for memory files —
WebConfig.memory_pathandWebConfig.short_term_memory_pathnow default viatimestamp_app_core.resolve_memory_path/_short_term_path. Test:tests/test_web_paths_resolvers.py. - v0.1.52 (2025-11-15): Memory diffs — on
replace_memory, NLCO now prints a minimal unified diff to the console after a successful replacement. Kept implementation tiny (usesdifflib.unified_diff). Test:tests/test_memory_replace_diff.pyasserts diff headers and changed lines. - v0.1.53 (2025-11-15): Added
dspy_programs/concept_worldmodel_experiment.py, a minimal concept-worldmodel experiment using DSPy + DeepSeek. Concepts are Pydantic models, tagging uses a DSPySignaturewith Pydantic output, and metrics cover tagging quality plus concept-structure via logistic regression. New tests:tests/test_concept_worldmodel_experiment.pyexercise synthetic data generation, theConceptTaggerPydantic path, and the metric helpers. - v0.1.54 (2025-11-15): Made
dspy_programs/concept_worldmodel_experiment.pyexecutable (shebang + chmod) so it can be run directly as./dspy_programs/concept_worldmodel_experiment.pyonce dependencies and environment are configured. - v0.1.55 (2025-11-16): Added
dspy_programs/concept_world_model.py, a multi-step concept world-model + control experiment. It simulates a hidden-state reactor env, tags STATE concepts via an LLM, represents ACTION concepts in the same concept universe, fits a linear RewardModel on discounted returnsG_t ≈ f([state_concepts_t, action_bits_t]), discovers high-reward concept pairs, asks the LLM to mint new abstract STATE concepts from those pairs, re-tags with the expanded schema, and runs a greedy actor that chooses actions by argmax predicted discounted reward. - v0.1.56 (2025-11-16): Added
dspy_programs/concept_world_model_v3.py, an updated world-model + control script that fixes the pre-/post-action mismatch (training on PRE-action observations so Q(s_t, a_t) semantics are consistent), increases meltdown frequency via more aggressive glitch dynamics and lower thresholds, and uses a fully unified concept feature space:X_tis a K-dimensional vector over all concepts (STATE bits from the LLM, ACTION one-hots from the env, MODEL concepts currently zeroed). It computes Monte-Carlo returnsG_tunder a random policy, fits a linear RewardModelG_t ≈ f(X_t), and acts greedily by embedding current STATE bits + candidate ACTION bits intoX_tand taking argmax predicted return. Default run is small (num_episodes=5,max_steps=12) to keep LLM calls manageable. - v0.1.57 (2025-11-17): Updated
dspy_programs/concept_world_model_v3.pyconcept encoding. STATE concept activations now use a ternary scheme in training data:1= present,-1= explicitly absent,0= missing / not defined at that time (e.g. concepts introduced later). The LLM still returns boolean activations; the tagger maps them into {-1, 0, 1}. Future-occupancy analysis now treats presence asvalue > 0when building discounted occupancy signals, soS_j(t)remains “discounted future presence” even with the richer encoding. RewardModel features embed these ternary STATE values directly; ACTION concepts remain one-hot (0/1). Running v3 still requires valid OpenRouter credentials for DSPy. - v0.1.58 (2025-11-17): Switched the default LM for
concept_world_model_v3to Gemini 2.5 Flash over OpenRouter (openrouter/google/gemini-2.5-flash) and added a--lmCLI flag so the model can be overridden (e.g.--lm openrouter/deepseek/deepseek-reasoner). DSPy is now configured insidemain()from this flag; the module still defines a default LM at import time for ad-hoc use, but script runs will reconfigure it based on--lm. Without a validOPENROUTER_API_KEY, runs will fail during tagging or concept creation. - v0.1.59 (2025-11-17): Added
tests/test_concept_world_model_v3.pyto cover key pieces of the v3 world-model script without calling real LMs. Tests stub the DSPyPredictcall to feed a fixedactivations_jsonintoLLMConceptTagger.tag_stateand assert the ternary encoding ([1, -1, 0]for present / absent / missing). Additional tests exerciseEpisodeDataset.build_discounted_reward()for (a) a no-meltdown episode (checking discounted Monte Carlo returns) and (b) an episode that ends in meltdown (reward at the meltdown step is 0 and earlier steps do not see any future reward beyond that point). A fourth test patchesLogisticRegressionandConceptCreatorso that_analyze_concept_future()deterministically appends a new meta-concept to theExperiment.universe, verifying that new STATE concepts are added correctly without depending on real model weights. Two more tests assert that malformedactivations_json(invalid JSON or wrong schema) causes PydanticValidationErrorto propagate fromtag_state, so schema violations fail loudly instead of being silently treated as zeros. Two further tests cover future-occupancy analysis: one asserts that when all concept future-occupancy targets are constant,_analyze_concept_future()prints the “all zero/constant” message along with per-conceptymeans (always 0/1); the other constructs a mixed target for a single concept and asserts that this message is not emitted. All eight tests pass withpytest -q tests/test_concept_world_model_v3.py. - v0.1.60 (2025-11-17): Made Pydantic validation failures in the concept-tagging path very visible at runtime.
LLMConceptTagger.tag_statenow wrapsConceptActivations.model_validate_json(...)in atry/except ValidationErrorblock; on failure it usesrichto draw a red rule with a clear message, prints the rawactivations_jsonpayload, and then re-raises the original exception. This keeps the behavior non-fallback (errors still fail fast) while making it obvious in logs when the LLM returned malformed or schema-incompatible activations. - v0.1.61 (2025-11-17): Relaxed the default cosine-similarity threshold for future-occupancy meta-concept creation.
Experimentnow takes acorr_thresholdparameter (CLI flag--corr-threshold, default0.0);_analyze_concept_futureuses this instead of a hard-coded0.8. With the default, any positively correlated pair (sim > 0) is eligible, but we still cap additions withmax_newand keep the “no structure” path when weight vectors are all zero.Experiment.run_greedy_actor_demoalso printsconcepts_added_totalandsamples_totalin each step, and_analyze_concept_futurereports how many concepts were created in that analysis plus the running total. To avoid sklearn shape errors after adding concepts,_greedy_actionresets theLinearRegressioncoefficients when the feature dimension changes. - v0.1.62 (2025-11-17): Removed vestigial
baseline_*fields fromrun_greedy_actor_demooutput; the earlier Monte Carlo warmup (which had populatedbaseline_overall_meanandbaseline_action_means) is gone, so those baselines were always 0/empty and misleading. Tests now assert that per-step logs contain nobaseline_mean_Gorbaseline_action_meansstrings. A--difficultyCLI flag was added toExperiment/ReactorEnvto scale instability: difficulty >1.0 multiplies glitch probability, scales stress/margin drift, and tightens meltdown thresholds slightly; difficulty <1.0 does the opposite. Default remains 1.0. - v0.1.63 (2025-11-17):
ReactorEnvnow distinguishes between basedifficultyand a per-stepcurrent_difficulty()which ramps up with the episode progress (step_idx / max_steps) and resets onreset(). Glitch probability, drift, and meltdown thresholds usecurrent_difficulty()rather than the raw base value, and the per-step log line printsdifficulty=<current>so you can see the ramp. The reward model is refit once per episode at the end, using Monte-Carlo returns over that episode; a compact summary of the fit (samples,mean_G, and top 3 weights) is logged after each episode.RewardModel.predictpads/truncates feature vectors to the learned feature dimension instead of resetting the model when new concepts are added; if the model has never been fit, it behaves as a zero predictor. Tests cover padding/truncation, meltdown behavior, epsilon-greedy exploration, operator notes (including steam/turbine hints), and the reward-model fit log line. The reward model now usesLasso(alpha=0.01)instead of plainLinearRegression/Ridgeto encourage sparse concept weights;Experimenttracks how often each concept’s reward-model coefficient is (effectively) zero across fits, and the fit summary prints outalways_zero=[...]as a list of concepts that have been zero in every fit so far (natural pruning candidates). A new--max-conceptsCLI flag (default 15) sets a hard cap on total concepts; when exceeded, the most frequently zero-weight STATE concept is dropped after each episode, never dropping ACTION/MODEL concepts. - v0.1.64 (2025-11-17): Simplified the base STATE concept definitions in
concept_world_model_v3to be short and generic (“core under high stress”, “safety margin small”, “channel has frequent disturbances”, “operator annoyed/angry”, “situation clearly dangerous”). IDs are unchanged (STRAINED_CORE,THIN_MARGIN,STORMY_CHANNEL,AGITATED_OPERATOR,CRITICAL_MODE) so tests and downstream logic remain valid, but prompts to the LLM are now less opinionated and easier to swap out for other environments.
Next Steps (2025-11-13)
- 90a. Add a test verifying artifact does not auto‑scroll when
TIMESTAMP_AUTO_SCROLL=0(symmetry with constraints). Recommended. - 90b. Optional: CLI/env toggle to select artifact top/bottom; add 1–2 tests.
- 90c. Consider
constraints_io.append_daily_line(now, message)helper to encapsulate last‑date detection; 1 unit test.
Things learned (2025-11-13)
-
Align paths across headless + TUI to avoid surprises; using the shared resolver eliminates the repo-root dependency.
-
Minimal guard (not a broad fallback): treating missing artifact as empty input allows first-run without creating files ahead of time. Things learned (2025-11-15)
-
Concept-worldmodel experiments live under
dspy_programs/concept_worldmodel_experiment.py; they rely on DSPy’s Pydantic integration (Pydantic models as input/output fields) and scikit-learn for simple logistic-regression structure probes. Tests stub the DSPyPredictcall so no real LM traffic occurs; when adding similar experiments, follow this pattern to keep tests fast and offline-friendly. -
When adding new Python scripts meant to be run directly (not just imported), add a shebang and ensure the file has the executable bit set in the workspace so
./path/to/script.pyworks without extra chmod steps. -
Running
./dspy_programs/concept_worldmodel_experiment.pywithout OpenRouter credentials currently fails with a litellmAuthenticationError(HTTP 401). This is expected; configureOPENROUTER_API_KEY(and any other dspy/OpenRouter settings) in the environment before running the full experiment to avoid noisy stack traces and repeated failures. -
Invoking the concept-worldmodel script via
zsh -ic ./dspy_programs/concept_worldmodel_experiment.pydoes not change this requirement; an interactive shell run still needs valid OpenRouter credentials. A previous run was aborted mid-call, but after sourcing~/.env_api_keysin bash the script completed end-to-end and printed tagging + structure metrics, confirming the wiring works with a valid key. -
The concept-worldmodel script seeds both Python’s
randomand NumPy (random.seed(42),np.random.seed(42)) at the start ofmain(), so the synthetic dataset (observations + ground-truth labels) is reproducible across runs. LM outputs may still vary depending on the model’s temperature/config. -
The concept-worldmodel script now prints the full concept list once at startup via
print_concepts()(id + definition for each concept) before generating data. Tests (test_print_concepts_lists_ids) assert this helper emits all concept IDs, so future changes to the concept set must keep the description output in sync. -
The concept-worldmodel script logs the first few examples (indices, observations, true labels, and predicted activations) to
.nlco/meta/concept_worldmodel_samples.jsonlvialog_samples_jsonl(). Tests (test_log_samples_jsonl_writes_lines) cover the helper; when debugging or comparing runs, inspect this JSONL rather than scrolling back through the full console output. -
Concept-worldmodel hyperparameter search:
- For the structure models (
C_j ~ other concepts), we now run a tiny random hyperparameter search over logistic regression settings once, globally, instead of per concept:- We sample three
(C, max_iter)pairs withC ~ 10**U[-2,2]andmax_iter ~ U{200..800}, fit a model for each concept and each trial, and aggregate validation CE/acc/AUC + total ΔCE across all non-degenerate concepts. - We print a compact table (
Hyperparam search for structure models (shared across concepts)) showing all trials and their average scores plussum_ΔCE, then select the best trial bysum_ΔCEand reuse its(C, max_iter)for all per-concept reports and ΔCE_self.
- We sample three
- The usefulness summary is labeled “ΔCE_self (predictable from others)” and sorted by ΔCE_self descending, with
frac_of_totalshowing each concept’s share of the total ΔCE_self sum. A one-line explanation clarifies thatΔCE_self = baseline_CE − best_CEfor predicting that concept from the others, so higher values mean a concept is more structured/predictable in the learned concept space.
- For the structure models (
-
Concept-worldmodel pipeline (detailed notes):
- High-level goal: let an LLM create and use abstract concepts as a compact world model, then use a separate ML layer to learn structure over those concepts (predictive relations, redundancies, higher-level abstractions). The outer objective is “make the system less surprised” (better prediction / compression), not just classify labels.
- Concept representation: concepts are treated as predicates over observations (
C(o) ∈ {0,1}now, optionally [0,1] later). In code we keep them as PydanticConcept(id, definition)objects so they can be passed into DSPy signatures dynamically and used as stable keys in matrices. - Binary vs scalar activations: v1 uses binary activations (0/1) for simplicity and for clean metrics; the design allows upgrading to scalar scores (
[0,1]confidence) later, with thresholding for discrete models and raw scores for richer ML. - What makes a concept “good”:
- Compression/MDL view: adding the concept should reduce
L(data | K) + L(K); i.e. it lets us encode many observations with fewer bits. - Predictive view: the concept should improve prediction of external targets or of other concepts.
- Invariants view: conditioned on the concept being true, some other properties become very stable (low entropy).
- Compression/MDL view: adding the concept should reduce
- Concept–observation matrix: for N observations and K concepts we build
Z ∈ {0,1}^{N×K}(or scores). On top of this, we run standard ML:- For each concept
C_j, train logistic regressionC_j ~ Z_{-j}; compare baseline CE (constant pos_rate) vs model CE to get ΔCE, plus acc/AUC. - Concepts that are highly predictable from others are likely composite/redundant; concepts that are hard to predict are more primitive/independent.
- The same machinery can be pointed at external labels (e.g. failure) to get concept importances.
- For each concept
- Causality vs compression: we treat MDL/compression + predictive fit as a pragmatic heuristic for structural learning in concept space, not as full causal discovery. Score-based causal-learning ideas inspire the design (simpler factorisations that still predict well) but we do not try to identify causal direction; we just learn “useful, stable dependencies” between concepts.
- Synthetic worlds: to avoid contamination from real-world knowledge, we use synthetic simulators with hidden numeric rules. The current script uses a reactor log world (hidden
stress, margin, glitches, tone→ textual logs + concept labelsSTRAINED_CORE,THIN_MARGIN,STORMY_CHANNEL,AGITATED_OPERATOR,CRITICAL_MODE), but the same pattern could be reused for other toy worlds. - Logprobs and fuzziness: the inner concept layer does not require token logprobs; we can treat the LLM as a black-box concept tagger. Logprobs are only needed if the outer objective is “reduce raw text perplexity given a world-model prefix”. For concept learning and structure analysis we work entirely in concept space (bits/scores + CE/ΔCE).
- Concept auto-regression and higher-level concepts: in the full design, we:
- Fit sparse models
C_j ~ other Cs to see which concepts cluster and which combinations have strong predictive power. - Use those patterns (e.g.
A & B & ¬C) as candidates for new higher-level concepts, then ask the LLM to propose a short natural-language definition from positive vs negative examples. - Keep both: a machine-side pattern (function of base concepts) and a compressed text definition (handle for the LLM).
- Fit sparse models
- Multi-step / future-aware concepts: for sequential data, instead of
failure_step_23we define trajectory-wide labels likeWILL_EVENTUALLY_FAIL(t)orFAIL_WITHIN_H(t)and treat them as concepts that depend on current state. This lets us train per-step “failure risk” concepts without exploding the concept set by time index. - Text definitions and compression: for new composite concepts, the definition prompt should not expose the raw logical pattern (A, B, C). Instead we show positive and negative examples and ask for a short abstraction, explicitly discouraging “laundry list” definitions. We can enforce compression by constraining length and rejecting definitions that are not materially shorter than spelling out the components. Things learned (2025-11-16)
-
The multi-step concept world-model script lives at
dspy_programs/concept_world_model.py. It keeps the LLM’s role minimal (STATE tagging + concept naming) and pushes control and value learning into a tiny linear RewardModel over concept bits, plus a greedy actor that selects actions by predicted discounted reward. -
The new script currently uses bare
dspy.InputField()/dspy.OutputField()(no positional...) in its Signatures; olderInputField(...)usage now fails against the local DSPy version withTypeError: InputField() takes 0 positional arguments but 1 was given. When adding new Signatures, follow the zero-positional-args pattern so they stay compatible. -
Running
python dspy_programs/concept_world_model.pywithout valid OpenRouter credentials fails during the first tagging pass with alitellm.AuthenticationError(“No auth credentials found” / HTTP 401) from the OpenRouter backend. This is expected; configureOPENROUTER_API_KEY(e.g., viasource ~/.env_api_keys) before running the full multi-step experiment, especially since it makes many LLM calls (one per step, plus concept-creation calls). -
The multi-step script also seeds
randomandnumpyinsideExperiment.run()(random.seed(42),np.random.seed(42)), so the synthetic trajectories (hidden state, actions under the random behavior policy, and meltdown events) are reproducible across runs. LLM outputs (STATE tags and new concept definitions) may still vary with model temperature/config. -
To keep runs tractable in this environment,
concept_world_model.pynow defaults tonum_episodes=5inmain(), which means ~50 LLM calls for the initial tagging pass plus a few more for the greedy-actor demo. Bigger runs (e.g., 40 episodes) are possible by editing theExperimentarguments or wiring a small CLI/env override, but be aware they scale linearly in LLM calls and wall time. -
For debugging transparency,
concept_world_model.pynow prints all LLM inputs and outputs for STATE tagging and concept creation: before each tagging call it prints the observation and the current STATE concept set (id + source), then prints the returned activation dict; before each new-concept call it prints the pattern concepts, pattern description, and a couple of positive/negative examples, followed by the generated concept id/definition. This is intentionally verbose and should be left as-is while we’re iterating on the world-model design; if it becomes too noisy for larger runs, consider gating it behind a simple verbosity flag or env var rather than removing it outright. -
The v3 script (
concept_world_model_v3.py) keeps the same broad structure but makes a few semantic fixes and runtime tweaks:- RL semantics: episodes are logged with PRE-action observations (
obs_t) and actionsa_t, and Monte-Carlo returnsG_tare computed from step t onward; the RewardModel is trained on(obs_t, a_t, G_t)so its predictions are genuinely Q-like for the random behavior policy. At inference time the greedy actor also operates on PRE-action observations, embedding STATE bits and each candidate ACTION bit into the unified concept vector before querying the RewardModel. - Environment tuning: glitch probability now ramps up more strongly with high stress/low margin, and the meltdown rule is relaxed to
stress > 85,margin < 25,glitches >= 2, which produces meltdowns with reasonable frequency over ~10–15 steps. This gives the RewardModel a more informative target signal than the earlier almost-always-survive regime. - DSPy integration: to stay compatible with the current DSPy version, v3 uses
InputField(desc=...)/OutputField(desc=...)and a JSON-string output (activations_json) for the tagging Signature, parsing into theConceptActivationsPydantic model viamodel_validate_json. Avoid using a field namedlabelson Signatures, since that collided with internal attributes in earlier experiments and produced confusing'function' objecterrors at runtime. - Runtime: a full v3 run with
num_episodes=5andmax_steps=12(60 decision steps, plus 3 greedy episodes) completes in ~5 minutes in this environment with the current OpenRouter-backed DeepSeek model. Increasing episodes/steps will raise runtime roughly linearly due to one LLM call per state tag, so any future CI-style tests for this script should stub the LM instead of hitting the real endpoint. - LLM I/O visibility: like the original
concept_world_model.py, v3 now prints all LLM inputs and outputs for STATE tagging and concept creation. For tagging it prints the STATE concept set once, then for each observation logs[LLM TAG INPUT v3](index + full text) and[LLM TAG OUTPUT v3](concept→0/1 dict). For concept creation (once we have strong pairs) it logs[LLM NEW-CONCEPT INPUT v3]with pattern concepts, description, and a couple of positive/negative examples, followed by[LLM NEW-CONCEPT OUTPUT v3]with the proposed id/definition. This is intentionally verbose for debugging; if it becomes cumbersome for larger experiments, consider adding a simple verbosity flag or env gate instead of removing the logs. - Output formatting: v3 now uses Rich (
rich.console.Console,rich.table.Table) for key sections. The random-policy simulation, baseline stats, and train/test split are wrapped inconsole.rule()headers and printed with color; baseline per-action returns are shown in a small Rich table. The greedy-actor demo prints one line per step with colored fields for the chosen action, predicted per-action returns, baseline means, and running average rewards (global + per-episode), plus colored meltdown/no-meltdown markers. The LLM tagging logs remain plainprint()calls to keep them simple; only the high-level experiment/RewardModel/actor sections use Rich styling. - Control knobs: the
--episodes/--epochsCLI arg now controls both (a) the number of random-policy episodes used to collect training data and (b) the number of greedy-actor episodes in the demo (we passnum_episodesinto both phases). An additional--epsilonarg toggles epsilon-greedy exploration during the greedy phase; with--epsilon > 0, each step chooses a random action with probability ε instead of the argmax predicted return. This keeps the model-learning phase Monte-Carlo over a random policy, while allowing exploration in the evaluation/control phase without touching the RewardModel itself. - Environment hardness + reward: we simplified away the separate random warmup and now start directly in ε-greedy episodes with a trivial zero-initialized RewardModel. The env gained a simple
demandvariable ("low"/"high") which is exposed in the observation text (“grid demand is modest” vs “grid demand is elevated; operators are asked to keep output up”). Reward is now demand-sensitive: meltdown yields a large penalty; otherwise we add a base survival reward plus an “output” term (push > steady > cool) that is rewarded when demand is high and mildly penalized when demand is low if output is far from a moderate level. This creates a minimal, sensible trade-off where “always cool” or “always push” is no longer obviously best; the agent must read both STATE concepts and demand text to balance safety vs output over time. - Minimal implementation scope: the current script covers the first step only:
- Hand-defined concepts (5 reactor concepts).
- Synthetic data with hidden numeric rules.
- LLM tagging vs ground truth (acc/F1).
- Concept-from-concept structure via logistic regression (ΔCE, acc, AUC).
- A small JSONL log (
concept_worldmodel_samples.jsonl) with a few observations, labels, and predicted activations for inspection. - No concept importance ranking, automatic concept creation, MDL selection, or sequence modeling yet—those are future layers on top of the same representation.
- RL semantics: episodes are logged with PRE-action observations (
-
v0.1.61 (2025-11-17): Updated the base STATE concepts in
dspy_programs/concept_world_model_v3.pyto be simple, generic text features instead of reactor-specific ones. The new seed vocabulary isTEXT_LENGTHY,STRUCTURED_TEXT,TECHNICAL_TONE, andSPECIAL_SYMBOLS, with short definitions about length, structure, technicality, and unusual symbols. This better matches the intent to have schema-level concepts like “text length / structured text / technical / special symbols” as the starting point, and keeps the LLM prompts more portable across different simulated worlds. Tests for v3 still pass since they only depend on counts/shapes and not on the specific IDs. -
v0.1.62 (2025-11-17): Reduced L1 regularization strength in the v3 RewardModel from
alpha=0.001toalpha=0.0001(Lasso(alpha=0.0001, max_iter=10000)inconcept_world_model_v3.py). This keeps the sparsity signal for concept pruning but lets more weight mass survive, which should make Q-value fits less biased under small data.tests/test_concept_world_model_v3.pystill passes (21 tests); sklearn still emits convergence warnings in a couple of tiny synthetic tests, which we accept for now to keep the implementation minimal. -
v0.1.63 (2025-11-17): Switched
TagConceptsinconcept_world_model_v3from a raw JSON-string output (activations_json) to a typedConceptActivationsoutput field.LLMConceptTagger.tag_statenow callsConceptActivations.model_validate(out.activations)instead ofmodel_validate_json, so DSPy/Pydantic handle the structure directly and we no longer ask the LM to hand-build JSON. Tests were updated to stubout.activationswith Python objects instead of JSON strings, and two error-path tests now assert that malformedactivationsvalues still raiseValidationError. This keeps the “fail loudly on bad schema” behavior while reducing the chance of brittle JSON formatting bugs like strayf{in the payload.tests/test_concept_world_model_v3.pycontinues to pass (21 tests), and a 3-episode manual run with Gemini 2.5 Flash completed without tagger validation errors. -
v0.1.64 (2025-11-17): Switched the v3 RewardModel from L1 (
Lasso) to L2 (Ridge) while keeping the same concept-importance and zero-weight tracking logic. This keeps the model strictly linear but removes the hard sparsity assumption, which should make Q-value fits smoother under small, noisy datasets. We still treat coefficients with |w| < 1e-6 as “effectively zero” for pruning stats.tests/test_concept_world_model_v3.pystill passes (21 tests) after the change. -
v0.1.65 (2025-11-17): Changed v3’s concept-pruning rule: when
max_conceptsis exceeded, we now drop the STATE concept with the lowest average importance under the RewardModel, rather than the one with the highest zero-weight count.Experimenttracksconcept_importance_sumsandconcept_importance_updatesand computes a simple mean score per concept; pruning chooses the minimum of that mean over LLM concepts only. The console message now reportsavg_importance=...instead of zero-counts. Testtest_max_concepts_drops_lowest_importance_state_conceptwas updated to synthesize importance sums and assert that the lowest-importance concept is removed when pruning triggers; the v3 test module still passes (21 tests). -
v0.1.66 (2025-11-17): Tuned v3’s RewardModel Ridge regularization; current setting is
alpha=0.1, a mild L2 penalty that stabilizes fits without making weights overly small. This still behaves like a plain linear Q-model in concept space but avoids some of the ill-conditioning seen withalpha=0.0.tests/test_concept_world_model_v3.pycontinues to pass after the change. -
v0.1.67 (2025-11-17): Added a global noise factor to the v3 reactor environment and CLI.
ReactorEnvnow takesnoise(>=0) and scales all stochastic dynamics with it: the random uniform deltas forstress,margin, andoutputinstep()are multiplied bynoise, glitch probability is scaled bydifficulty * noise, and demand-flip probability ismin(1, 0.2 * noise).Experimentacceptsnoiseand forwards it, andconcept_world_model_v3.pyexposes--noise(default 1.0). A new testtest_noise_factor_scales_push_dynamicsseeds Python’s RNG and asserts that a PUSH step withnoise=2.0produces a larger |Δstress| than withnoise=0.1under the same underlying random draw. The v3 test module now has 22 passing tests. -
v0.1.68 (2025-11-17): Extended the v3 reactor observation string to include the exact numeric power output.
ReactorEnv._make_observationnow appends(measured reactor output={self.output:.3f}).to the operator note, so logs include both qualitative steam/turbine hints and a precise scalar in the same sentence. New testtest_observation_includes_exact_power_outputsets a fixedenv.outputand asserts that the formatted value appears in the observation. The v3 tests now total 23 and all pass. -
v0.1.69 (2025-11-17): After each episode in v3, the summary block now prints a running average episode reward across all episodes seen so far, in addition to per-episode totals and the cumulative sum. Specifically,
run_greedy_actor_democomputesrunning_avg = cumulative / len(episode_summaries)and logsRunning avg episode reward: ...after the “Episode totals so far” list and “Cumulative total reward”. New testtest_episode_summary_includes_running_avgstubs the env/policy, runs two episodes, and asserts that the string appears in the captured console output. The v3 test module now has 24 passing tests. -
v0.1.70 (2025-11-17): Changed v3’s greedy-learning phase to use a replay-style buffer and always train the RewardModel on all collected samples, not just the last episode.
Experimentnow keepsmemory_states(dicts concept_id→value),memory_actions, andmemory_returns; after each episode it computes discounted returns for that episode, appends them to the buffers, then calls_build_training_data()to embed every stored sample into the current concept universe (STATE bits by id, ACTION one-hot, missing concepts as 0).RewardModel.fitis then called on this full dataset each time. This guarantees that earlier episodes continue to influence Q-value estimates even after new concepts are introduced, without misaligning feature indices. Tests still pass (24); the reward-fit logging now reportssamples=as the total replay size instead of per-episode length. -
v0.1.71 (2025-11-17): Added a small regression test to ensure that the numeric power output in v3 stays in sync with the operator note.
test_power_output_in_note_tracks_updatessetsenv.outputtwice (0.500, then 1.250) and checks that_make_observation()shows the corresponding(measured reactor output=...)substring each time, confirming the note always reflects the latest output. The v3 test module now has 25 passing tests. -
v0.1.72 (2025-11-17): Fixed a v3 pruning quirk where the meta-concept created in a given future-occupancy analysis could be immediately dropped by the
max_conceptsbudget.Experimentnow tracksnew_state_concepts_last_analysis(reset at the start of_analyze_concept_future) and appends any newly added STATE concept ids to it; the pruning candidates list excludes those ids, so only pre-existing STATE concepts are considered for removal in that episode. This keeps freshly minted concepts around for at least one training/pruning cycle. All v3 tests still pass (25). -
v0.1.73 (2025-11-17): Added
test_newly_created_concepts_not_pruned_immediatelyto the v3 tests. It stubsConceptCreator.createto return a known meta-concept id, runs_analyze_concept_future()to add it, seedsconcept_importance_sumsso an older base concept is weakest, and then reconstructs the pruning candidate set as inrun_greedy_actor_demo. The assertion ensures the new concept id is not in the candidates, locking in the “don’t immediately drop the concept we just created” behavior. The v3 test module now has 26 passing tests. -
v0.1.74 (2025-11-17): Tightened the v3 meta-concept prompt to prefer “cause-of-correlation” concepts rather than simple conjunctions.
ProposeNewConcept’s docstring and field descriptions now explicitly ask the LLM to describe a plausible underlying situation or cause that would make the two pattern concepts tend to be true together (given their high future-occupancy correlation), and to avoid restating or conjoining the input IDs. Thepattern_descwe pass fromPairwiseConceptDiscovery.discover()was updated to mention strongly correlated discounted occupancy and to ask for an explanation of that shared cause. No code flow changes; tests still pass (26).
Related Documents
Browser-only development
This document provides guidance for AI assistants working on the Image MetaHub codebase.
Claude Agents — Reference & Recommendations
Quick guide to available agents. Pick the one that best matches your task.
Golden DKG Prototype -- Master Plan
Rust prototype of the Golden non-interactive Distributed Key Generation protocol.
Swarms Examples Index
A comprehensive index of examples from the [Swarms Framework](https://github.com/The-Swarm-Corporation/swarms-examples).