fix(kb): repair two unsound rules and drop captured runtime shims

Audited prolog-sessions/kb.pl, which had been appended to all session without
anyone checking it loads or reasons correctly. Four problems.

may_depend_on/2 was type-confused. It read

    may_depend_on(A,B) :- crate(A,C,D), \+forbidden(A,B).

with C and D unused and B never constrained, so may_depend_on(improvise_core,
banana) succeeded. forbidden/2's second argument is a CONCERN (ui, io,
ratatui), not a crate, so the rule compared a crate against a concern and
called it a dependency check. Replaced by may_use/2 over an explicit
concern/1, plus crate_depends_on/2 facts read from the Cargo.toml files and
an illegal_dependency/2 rule that actually checks for inverted layering.

before_fixing/2 had a dead guard: `A=C` bound A to a fresh variable and
constrained nothing, so it succeeded for any first argument. It now requires
the subject to be a recorded bug fix.

The first sixteen lines were the MCP runtime's own predicates — sleep,
js_eval, future, await_any and friends — captured into the file by a
saveSession. Not project knowledge, and the reason every consult warned about
overwriting sleep/1.

Nine predicates are appended chronologically and so are scattered through the
file. Added :- discontiguous declarations rather than reordering, since the
chronological shape is what makes the file readable as a work log.

Also verified every location/3 fact still names a symbol that exists in the
tree, and that no predicate has conflicting values for the same key.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V69PGQfMJuGRmtSWpfVAqn
This commit is contained in:
Edward Langley
2026-08-30 10:15:01 -07:00
parent 3697171c0f
commit a406b52a37
+60 -24
View File
@@ -1,19 +1,18 @@
sleep(A) :-
wasm_generic:host_rpc(sleep(A)).
delay(A) :-
wasm_generic:host_rpc(delay(A)).
console_log(A) :-
wasm_generic:host_rpc(console_log(A)).
js_eval(A,B) :-
wasm_generic:host_rpc(js_eval(A,B)).
js_eval_json(A,B) :-
wasm_generic:host_rpc(js_eval_json(A,B)).
future(A,B) :-
wasm_generic:host_rpc(future(A,B)).
await_any(A,B,C) :-
wasm_generic:host_rpc(await_any(A,B,C)).
future_cancel(A) :-
wasm_generic:host_rpc(future_cancel(A)).
% Improvise project knowledge base.
%
% Facts are appended chronologically as work happens, so most predicates are
% spread through the file rather than grouped; these declarations keep that
% shape without warnings on every consult.
:- discontiguous crate/3.
:- discontiguous layer/2.
:- discontiguous location/3.
:- discontiguous invariant/2.
:- discontiguous doctrine/2.
:- discontiguous work_done/3.
:- discontiguous work_followup/3.
:- discontiguous decision/3.
:- discontiguous wire_carries/3.
crate(improvise_core,'crates/improvise-core','Model, View, Workbook, formatting; no UI/IO').
crate(improvise_formula,'crates/improvise-formula','formula parser, AST, parse_formula').
crate(improvise_io,'crates/improvise-io','improv save/load, CSV/JSON import; no UI/commands').
@@ -85,14 +84,44 @@ session_close(4,'git merge origin/main, bd dolt push, git push').
session_close(5,'clean up stashes and pruned branches').
session_close(6,'verify committed AND pushed').
session_close(7,'hand off context').
crate_of_layer(A,B) :-
layer(A,B).
may_depend_on(A,B) :-
crate(A,C,D), \+forbidden(A,B).
where(A,B,C) :-
location(A,B,C).
before_fixing(A,B) :-
A=C,doctrine(bug_first,B).
crate_of_layer(Layer,Crate) :-
layer(Layer,Crate).
where(What,File,Symbol) :-
location(What,File,Symbol).
% The concerns a crate is allowed to touch. `forbidden/2`'s second argument is
% a CONCERN, not a crate -- the old may_depend_on/2 conflated the two and so
% accepted any term at all for its second argument.
concern(ui).
concern(io).
concern(commands).
concern(ratatui).
concern(crossterm).
may_use(Crate,Concern) :-
crate(Crate,_,_), concern(Concern), \+ forbidden(Crate,Concern).
% The real crate graph, read from the Cargo.toml files (2026-08-30).
crate_depends_on(improvise_core,improvise_formula).
crate_depends_on(improvise_io,improvise_core).
crate_depends_on(improvise_io,improvise_formula).
crate_depends_on(improvise_command,improvise_core).
crate_depends_on(improvise_command,improvise_formula).
crate_depends_on(improvise_command,improvise_io).
crate_depends_on(improvise_main,improvise_core).
crate_depends_on(improvise_main,improvise_formula).
crate_depends_on(improvise_main,improvise_io).
crate_depends_on(improvise_main,improvise_command).
depends_transitively(A,B) :- crate_depends_on(A,B).
depends_transitively(A,B) :- crate_depends_on(A,X), depends_transitively(X,B).
% A dependency edge that would invert the layering.
illegal_dependency(A,B) :-
crate_depends_on(A,B), depends_transitively(B,A).
before_fixing(Bug,Step) :-
work_done(Bug,bug_fix,_), doctrine(bug_first,Step).
work_done('improvise-8tk',bug_fix,'ZZ/:wq quit even when the save failed, silently discarding the model. Save now aborts the effect batch on failure; save_to_current_path/1 returns Result<String,String>; three tests in ui/effect.rs.').
@@ -228,3 +257,10 @@ groom_check(stale_citation,'file:line references rot; resolve each against the t
groom_check(dissolved_premise,'grep for the pattern an issue targets before working it -- improvise-ont described 11 call sites of a pattern that no longer occurs anywhere').
doctrine(mentions_are_not_edges,'never bulk-add dependency edges from prose mentions; direction must be read per pair, and blind addition can invert real edges or create cycles').
work_done('backlog-groom-2026-08-30',groom,'closed improvise-ont as obsolete; raised 7 priority inversions to P2; added 2 verified dependency edges out of 21 candidates; corrected stale refs on ai7/ct5/gdl and stale assumptions on jb3/git.').
% ── KB hygiene (audited 2026-08-30) ─────────────────────────────────────────
kb_issue_found(host_rpc_shims,'sleep/delay/console_log/js_eval/future/await_any/future_cancel were the MCP runtime own predicates, captured into this file by a saveSession. Removed: not project knowledge, and the source of an overwriting warning on every consult. If saveSession re-adds them, strip them again.').
kb_issue_found(may_depend_on_type_confusion,'may_depend_on(A,B) :- crate(A,C,D), \\+forbidden(A,B). C and D were singletons and B was never constrained, so may_depend_on(improvise_core, banana) succeeded. forbidden/2 second argument is a CONCERN, not a crate. Replaced by may_use/2 over an explicit concern/1, plus real crate_depends_on/2 facts read from the Cargo.toml files.').
kb_issue_found(before_fixing_dead_guard,'before_fixing(A,B) :- A=C, doctrine(bug_first,B). A=C bound A to a fresh variable and constrained nothing, so the rule succeeded for any first argument. Now requires work_done(Bug,bug_fix,_).').
kb_issue_found(discontiguous,'nine predicates are appended chronologically and so are scattered; added :- discontiguous declarations rather than reordering, because the chronological shape is the point.').
doctrine(kb_is_code,'the knowledge base is executable, so audit it like code: check rules actually reject what they should, and check location/3 symbols still exist in the tree').