chore(beads): drop memories from the tracked export, let auto-export own it
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled

bd 1.3.0's auto-export scope excludes memories even with
export.include-memories: true, so while .beads/issues.jsonl carried them
every mutating bd command hit the shrink guard and auto-export never
updated the file. Set export.include-memories false via bd config set and
re-exported without them; auto-export now writes and stages the file
unattended, verified by a subsequent bd write landing with no warning.

Memories are unaffected -- they live in Dolt and replicate via bd dolt
push. Dropped the now-wrong bd-export-must-include-memories memory and
replaced it with autoexport-owns-issues-jsonl plus the never-hand-edit
rule.

kb.pl: replaced the stale export_drops_memories_by_default decision with
autoexport_owns_the_tracked_export, memories_in_the_file_freeze_autoexport
and plain_export_rewrites_the_bookkeeping. Maintained through the MCP;
host_rpc shims stripped again per kb_issue_found(host_rpc_shims).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Edward Langley
2026-09-19 13:25:38 -07:00
parent 59f503a22a
commit 80d0f9fdcf
3 changed files with 5 additions and 20 deletions
+1 -1
View File
@@ -55,4 +55,4 @@ sync.remote: "git+ssh://git@git.fiddlerwoaroof.com/u/edwlan/improvise.git"
export:
auto: true
git-add: true
include-memories: true
include-memories: false
-16
View File
@@ -165,19 +165,3 @@
{"_type":"issue","id":"improvise-3gy","title":"4.2 Enable GitHub Pages","description":"Enable Pages in GitHub repo settings: source=main branch, folder=/docs. Verify site is live at the GitHub Pages URL.","status":"deferred","priority":4,"issue_type":"task","owner":"el-github@elangley.org","created_at":"2026-04-09T04:08:10Z","created_by":"Edward Langley","updated_at":"2026-07-30T23:06:32Z","defer_until":"2026-08-09T00:00:00Z","dependencies":[{"issue_id":"improvise-3gy","depends_on_id":"improvise-e61","type":"blocks","created_at":"2026-04-08T21:09:32Z","created_by":"Edward Langley","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
{"_type":"issue","id":"improvise-e61","title":"4.1 Create docs/index.html landing page","description":"Vanilla HTML, single file, under 200 lines. Dark background, monospace headings. Embed asciinema-player from jsdelivr CDN. Sections: title/tagline, pivot cast, drill cast, formulas cast, import cast, install commands + GitHub link. Player config: rows 30, cols 100, theme monokai, autoPlay false, loop true.","status":"deferred","priority":4,"issue_type":"task","owner":"el-github@elangley.org","created_at":"2026-04-09T04:08:07Z","created_by":"Edward Langley","updated_at":"2026-07-30T23:06:31Z","defer_until":"2026-08-09T00:00:00Z","dependencies":[{"issue_id":"improvise-e61","depends_on_id":"improvise-d4w","type":"blocks","created_at":"2026-04-08T21:09:31Z","created_by":"Edward Langley","metadata":"{}"}],"dependency_count":1,"dependent_count":2,"comment_count":0}
{"_type":"issue","id":"improvise-kh8","title":"Phase 4: Landing page (optional)","description":"Create docs/index.html with embedded asciinema casts and enable GitHub Pages. Optional but recommended for launch.","status":"deferred","priority":4,"issue_type":"feature","owner":"el-github@elangley.org","created_at":"2026-04-09T04:05:53Z","created_by":"Edward Langley","updated_at":"2026-07-30T23:06:32Z","defer_until":"2026-08-09T00:00:00Z","dependencies":[{"issue_id":"improvise-kh8","depends_on_id":"improvise-3gy","type":"blocks","created_at":"2026-04-08T23:37:42Z","created_by":"Edward Langley","metadata":"{}"},{"issue_id":"improvise-kh8","depends_on_id":"improvise-e61","type":"blocks","created_at":"2026-04-08T23:37:41Z","created_by":"Edward Langley","metadata":"{}"}],"dependency_count":2,"dependent_count":0,"comment_count":0}
{"_type":"memory","key":"agent-issue-drift-pattern","value":"Beads issues created by agents (owner 'spot') can duplicate users actively in-progress work. Example 2026-04-16: improvise-dwe (Split App into AppState + App wrapper) was a coarser restatement of improvise-vb4 (Split AppState into ModelState + ViewState) which was already in_progress and assigned to the user; dwe was filed 2 days later by an agent unaware of vb4. Before filing a structural refactor, run bd search for keywords from its core concept AND check in_progress issues."}
{"_type":"memory","key":"app-new-calls-recompute-formulas-before-building-initial","value":"App::new calls recompute_formulas before building initial layout so formula values appear on first frame. render() test helper also calls recompute_formulas."}
{"_type":"memory","key":"bd-1-0-2-dev-embedded-dolt-dependencies","value":"bd version skew across machines silently empties the dependency graph in .beads/issues.jsonl. bd 1.0.2 (dev) embedded-dolt cannot read the dependencies table (missing depends_on_id column), so its export writes every issue with no dependencies array and zeroed dependency_count/dependent_count, and its issue lines carry no _type field. bd 1.1.1 (dev) — the local nix-profile build as of 2026-09-19 — reads them fine and exports all 151 edges plus _type. Since the JSONL is a git-tracked mirror regenerated by whichever machine exports last, a sync from the older machine reverts the whole graph; observed twice on 2026-09-19 (once from auto-export dropping memories, once from a conflict resolved by an old-bd export). Nothing is lost from the tracker itself: dolt keeps the edges and bd dolt push carries them. How to apply: after any sync that rewrites issues.jsonl, check 'edges' with a quick count and re-run 'bd export --include-memories -o .beads/issues.jsonl' from the 1.1.1 machine if it reads 0. Real fix is upgrading bd on the other machine."}
{"_type":"memory","key":"bd-auto-recognizes-beads-actor-currently-jeeves-as","value":"bd auto-recognizes $BEADS_ACTOR (currently 'jeeves') as the agent identity, so plain 'bd update \u003cid\u003e --claim' / 'bd close \u003cid\u003e' attribute correctly on their own. Why: the env var is set per-shell to identify which agent is acting, and bd reads it implicitly. How to apply: do not pass --assignee=$(whoami) or --assignee=$BD_ACTOR (BD_ACTOR is wrong — it's BEADS_ACTOR). Only pass --assignee=$BEADS_ACTOR explicitly when stealing a ticket already claimed by a different actor; everywhere else, omit it and let bd pick up BEADS_ACTOR automatically."}
{"_type":"memory","key":"bd-export-must-include-memories","value":"bd export omits 'bd remember' memories unless --include-memories (or --all) is passed. .beads/issues.jsonl carries issues AND the memories, so always regenerate it with: bd export --include-memories -o .beads/issues.jsonl. A plain 'bd export -o .beads/issues.jsonl' silently drops every memory record. Also: bd export orders each issue's dependencies array nondeterministically, so an unchanged re-export still shows a ~23-line diff -- compare with dependencies sorted before committing, and revert if nothing differs."}
{"_type":"memory","key":"compiler-exhaustiveness-theme","value":"Running theme across the 2026-04-16 refactor backlog: the compilers exhaustive-match check is being bypassed. (1) string compares against virtual-category names (improvise-2lh); (2) registered command names as strings vs Cmd::name() (improvise-9cn, improvise-61f); (3) minibuffer buffer_key strings threaded through 7 AppMode constructors vs command lookups (improvise-k8h); (4) AppMode per-variant logic scattered across 52 match sites in 8 files (improvise-2hi); (5) duplicated Axis display matches in tile_bar + category_panel (improvise-rml). Common fix: push dispatch onto a method on the enum/type so the exhaustive match has one home."}
{"_type":"memory","key":"drillintocell-strips-measure-coordinate-from-drill-key-when","value":"DrillIntoCell strips _Measure coordinate from drill key when it matches a formula target, so matching_cells finds raw data records instead of returning empty."}
{"_type":"memory","key":"dynamic-measure-formula-targets-are-dynamically-included-via","value":"Dynamic _Measure: formula targets are dynamically included via Model::measure_item_names() and effective_item_names(). add_formula no longer adds items to _Measure category. find_item_category falls back to formula targets. CommitFormula defaults target_category to _Measure."}
{"_type":"memory","key":"gen-grammar-example-generates-random-valid-improv-content","value":"gen-grammar example: generates random valid .improv content from pest grammar rules. Uses word pools for realistic output. pretty-print example: parses stdin and prints formatted output. Both in examples/ directory."}
{"_type":"memory","key":"keymap-parent-inheritance-added-linked-list-of-keymaps","value":"Keymap parent inheritance added (linked list of keymaps). All minibuffer modes use Binding::Sequence for Enter and Esc to include clear-buffer command. ClearBufferCmd registered in registry."}
{"_type":"memory","key":"lib-rs-created-to-enable-examples-to-import","value":"lib.rs created to enable examples to import from improvise crate. main.rs uses 'use improvise::*' instead of mod declarations."}
{"_type":"memory","key":"never-hand-edit-issues-jsonl","value":"NEVER hand-edit, revert, or 'git checkout' .beads/issues.jsonl -- beads owns that file, and a git checkout of it fires the beads post-checkout hook, which rewrites AND stages it. To change it, run a bd command.\n\nSecond, separate trap: bd 1.3.0's AUTO-export scope excludes memories even though this repo's .beads/config.yaml sets export.include-memories: true. Since the tracked file deliberately carries memories (commit 130ec4b), every mutating bd command hits the shrink guard -- 'refusing to overwrite ... contains N record(s) outside auto-export scope' -- and auto-export never updates the file. The guard is PROTECTING the memories, not malfunctioning. Consequence: new memories land in Dolt and replicate via 'bd dolt push', but reach issues.jsonl only through an explicit 'bd export --include-memories -o .beads/issues.jsonl', which is the remedy the guard message itself names.\n\nAlso: a plain 'bd export -o .beads/issues.jsonl' (no --include-memories) both strips the memories and rewrites .beads/export-state.json with memories:0. Never run the plain form here."}
{"_type":"memory","key":"persistence-index-and-dim-categories-are-never-written","value":"Persistence: _Index and _Dim categories are never written to .improv files. _Measure only persists non-formula items. Formulas targeting _Measure omit the [_Measure] suffix (it's the default). Parser defaults to _Measure when no [Category] suffix present."}
{"_type":"memory","key":"refactor-ticket-loc-estimates-are-inflated","value":"The '~N LOC collapsed' estimates on the 2026-04-16 deep-review refactor backlog are systematically optimistic, because they were written by counting duplicated lines without costing the abstraction that replaces them, and without diffing the call sites closely enough to notice what else varies.\n\nMeasured outcomes so far:\n- improvise-tvt: claimed a collapse, delivered -125 (the one that beat its estimate; it removed 165 near-identical call chains).\n- improvise-6t3: claimed ~-40, actual ~+24. The three commit commands varied in five dimensions, two of them semantic (post-commit landing mode; formula not trimming).\n- improvise-w3q: claimed ~-150, actual -39. Tab and Backspace varied across the seven modes as well as buffer/commit/exit, and the TextEntry builder itself cost ~62 lines.\n\nHow to apply: before starting one of these, diff the call sites yourself and count what actually varies — do not trust the ticket's list. Report the real delta in the close reason and correct the ticket's claim explicitly. The justification for these refactors is almost always a correctness invariant (one home for a rule, an impossible-to-omit pairing), not line count — lead with that instead of defending the estimate. Related: [[review-methodology-scoped-explore-agents]], which produced this backlog."}
{"_type":"memory","key":"review-methodology-scoped-explore-agents","value":"Deep-review batches work best with narrowly-scoped Explore agents (one per layer/principle lens: model+formula, command+ui, persistence+import, LoD violations, OCP violations). Prompt each with: (1) specific files with sizes, (2) the lens/principle to apply, (3) exactly how to report (count + file:line refs, prioritized, ~800 words). Parallel launches worked cleanly when file scopes did not overlap. Session 2026-04-16 produced a ~20-issue backlog this way."}
{"_type":"memory","key":"rust-toolchain-lives-in-direnv-flake","value":"The improvise Rust toolchain is provided by a Nix flake loaded through direnv (.envrc). Agent Bash tools do NOT inherit the direnv environment, so plain 'cargo fmt' and 'cargo clippy' fail with 'no such command' — the tools are present, just not on the bare PATH. Prefix with 'direnv exec .': 'direnv exec . cargo fmt --all', 'direnv exec . cargo clippy --workspace --all-targets'. Plain 'cargo build'/'cargo test' work because a system cargo is on PATH, which is why the gap looks like a missing component rather than a missing environment. Do not conclude 'rustfmt/clippy is not installed' — that was recorded incorrectly in earlier sessions and led to hand-diffing line lengths as a formatting proxy."}
+4 -3
View File
@@ -140,11 +140,12 @@ decision('improvise-cqi',view_state_carries_its_own_active_view,'reduce_view run
decision('improvise-sef',retroactive_ticket,'The three kb commits landed with no beads issue at all. Filed and closed 2026-09-19 during a commit/tracker reconciliation rather than left untracked, because the CI job they add is a durable capability someone will later ask the provenance of. Every other commit back to cc3889d already named a ticket that is closed with a close_reason.').
decision('backlog-groom-2026-09-19',render_cache_name_collision,'improvise-cqi landed RenderCache as a flat grid-only wire type, while six open migration tickets (35e, n10, pca, 764, jb3, edp) were written against a fat per-widget cache of the same name. Two objects, one name, and nothing in the tree complains until someone writes from_app. Filed improvise-rkeq to decide the shape and made it block 35e rather than silently rewriting six tickets to match whichever shape a groomer preferred.').
decision('backlog-groom-2026-09-19',retroactive_tickets_for_untracked_merges,'PR #7 (cloud-session tooling) landed 18 commits with no beads issue, the same gap the kb commits had. Filed improvise-3qz2 closed. Rule being followed: work that adds a durable capability -- a CI job, a hook, a script someone will later ask the provenance of -- gets a ticket even after the fact; a chore commit does not.').
decision('beads-maintenance-2026-09-19',export_drops_memories_by_default,'bd export omits the bd remember memories unless --include-memories (or --all) is passed, and .beads/issues.jsonl carries both. Regenerate it with: bd export --include-memories -o .beads/issues.jsonl. A plain bd export silently deletes every memory record from the tracked file and the diff reads as an ordinary re-export.').
decision('beads-maintenance-2026-09-19',git_hooks_bypass_the_shrink_guard,'bd own auto-export refuses to shrink the file (auto-export shrink guard: refusing to overwrite ... contains N record(s) outside auto-export scope), but the beads-installed git hooks run the plain export and bypass that guard. Observed 2026-09-19: a git checkout of an unrelated file fired post-checkout, which rewrote AND staged .beads/issues.jsonl with all 15 memories removed. Filed as a P2 bug.').
decision('beads-maintenance-2026-09-19',autoexport_owns_the_tracked_export,'user decision 2026-09-19: the tracked .beads/issues.jsonl carries issues ONLY and bd auto-export maintains it. export.include-memories was set to false via bd config set, which matches bd auto-export scope, so auto-export (export.auto + git-add) now writes and stages the file unattended. Memories live in Dolt and travel by bd dolt push, not by this file. Never hand-edit or git-checkout the file: a checkout fires the beads post-checkout hook, which rewrites AND stages it.').
decision('beads-maintenance-2026-09-19',memories_in_the_file_freeze_autoexport,'why the memories were dropped. bd 1.3.0 auto-export scope EXCLUDES memories even when config sets export.include-memories: true -- the shrink guard names them as \"outside auto-export scope\". While the file carried them, every mutating bd command hit the guard and auto-export never updated the file, so the guard was protecting the memories rather than malfunctioning. Two ways out: keep memories and maintain the file by explicit export forever, or drop them and let auto-export own it. Chose the second.').
decision('beads-maintenance-2026-09-19',plain_export_rewrites_the_bookkeeping,'bd export -o writes .beads/export-state.json as well as the file, recording the memory count it just wrote. So running the wrong export form desyncs the bookkeeping from config and changes what the shrink guard compares against later. Match the export form to export.include-memories rather than passing flags ad hoc.').
decision('beads-maintenance-2026-09-19',export_dependency_order_is_nondeterministic,'bd export emits each issue dependencies array in nondeterministic order, so a re-export of unchanged data still shows a ~23 line diff. Before committing, compare old against new with the dependencies arrays sorted; if nothing differs semantically, revert rather than commit the churn.').
decision('beads-maintenance-2026-09-19',flake_beads_pin_verified,'flake.nix pins beads to github:gastownhall/beads/v1.3.0 (flake.lock rev f45b249ce6b40ba62aecc03949e6371e8f7c79d8). nix build of that ref yields beads-1.3.0, matching the local bd 1.3.0 that writes .beads/issues.jsonl, so the pinned tool and the tracked export agree on format.').
decision('kb-normalization-2026-09-19',kb_normalized_to_mcp_canonical_form,'user decision 2026-09-19: kb.pl is maintained by the MCP, so it was saved in the MCP canonical form -- 563 lines to 399. Cost, measured by a no-op round-trip probe before committing: all 106 comment lines dropped, the 9 discontiguous directives dropped, multi-line rules reflowed onto one line, and anonymous _ arguments renamed to singleton variables (new trealla warnings, no semantic change). Every clause of every predicate survived and kb_consistent still passes. The chronological append shape that kb_issue_found(discontiguous) preserved is gone; clauses are now grouped by predicate, which is why the directives are no longer needed.').
decision('kb-normalization-2026-09-19',kb_normalized_to_mcp_canonical_form,'user decision 2026-09-19: kb.pl is maintained by the MCP, so it was saved in the MCP canonical form -- 563 lines to 391. Cost, measured by a no-op round-trip probe before committing: all 106 comment lines dropped, the 9 discontiguous directives dropped, multi-line rules reflowed onto one line, and anonymous _ arguments renamed to singleton variables (new trealla warnings, no semantic change). Every clause of every predicate survived and kb_consistent still passes. The chronological append shape that kb_issue_found(discontiguous) preserved is gone; clauses are now grouped by predicate, which is why the directives are no longer needed.').
decision('kb-normalization-2026-09-19',important_knowledge_is_asserted_never_commented,'user rule 2026-09-19: anything important goes in a fact, not a comment. Checked before normalizing -- every substantive comment was already asserted somewhere (cycle-safe closure in kb_issue_found(cycle_unsafe_closure), MCP leniency in kb_issue_found(mcp_lenience), the symbol-existence check in kb_external_check/1, fault injection in doctrine(check_the_checker)), so only section headers were lost. Re-run that check before any future save: prose that exists ONLY as a comment will not survive one.').
decision('kb-normalization-2026-09-19',audit_the_save_for_host_predicates,'saveSession serializes the whole clause database, including predicates the MCP runtime itself defines -- kb_issue_found(host_rpc_shims) records sleep/delay/console_log/js_eval/future/await_any/future_cancel being captured into this file that way once already. After every saveSession, grep the result for predicates that are not project knowledge and confirm the clause counts per predicate are unchanged, because loadProgram REPLACES a predicate rather than appending to it and a short payload silently drops the rest.').
wire_carries(command,both_directions,'user intent up, projection commands down').