Repository navigation
Merged
Conversation
… snapshot its writes committed against A claim guarded on a candidate withdraws the candidate by succeeding, so a `.why` sent after the claim explains a row that is gone. A `.why`, `.why full` or `.why_not` may now be the last statement of a program that writes. It runs after the commit on the snapshot the commit applied to: the knowledge graph just before the program's changes, on which the program's reads were validated to still hold, so it explains the state the guard passed on. A restage moves the proof with the guard; later writes never change it. The reply keeps the statements' message rows and carries the proof in `proof_trees`; each proof tree now names the snapshot `revision` it was evaluated on. A proof that is not the last statement, or one in a program that also holds a query, is rejected with `unsupported` before anything runs. Programs without writes run `.why` as before.
jsam
marked this pull request as ready for review
October 4, 2026 19:22
This was referenced Oct 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
please continue developing the input layer engine so we make sure that the symbolic engine is ready to go live and completely production ready while we develop the GoTM uh, market.
(Context: the captain approved the neurosymbolic decision engine plan; its milestone 'Engine readiness' makes the symbolic engine production-ready for the hosted decision API. This task is item EN-7: proof pinned to the commit snapshot: a proof statement at the end of a program, evaluated on the snapshot the guard passed on, returned in the reply. M1 critical path — why: the stored proof is the proof of the state the gate checked. The CLM evaluation's
run_okworkaround is the shim until then: a claim guarded on eligibility (eligible, which contains!attempt) withdraws eligibility by succeeding, so.why ?eligible(...)sent after the claim explains an absent row; the workaround proves a separate right-to-continue view,run_ok(S, T, Id) <- attempt(S, T, Id), need(S, T), tool_ready(T), after the claim instead. In the decision flow the guarded program is the token form —-il_txn(""), +il_txn("t-…") <- <guard>then ghost-anchored conditional inserts-il_ghost(0), +attempt(...) <- il_txn("t-…"),+decision(...),+decision_score(...), then-il_txn(T) <- il_txn(T), T = "t-…"— where the guard is evaluated once, every conditioned statement lands or none does, and the reply's per-statement counts say whether the claim won; the proof (for example of the candidate the claim was guarded on: the facts, the absences and the rules that made the action allowed) is stored as the decision's proof.)What Changed
.why,.why fullor.why_notcan now be the last statement of a program that writes. It runs after the commit, on the snapshot the writes committed against:ProgramCommitnow carries abasesnapshot, and if the program is re-staged after going stale, the proof uses the new base. The reply keeps one message row per statement and its per-statement counts, and puts the proof inproof_trees. If the proof fails, its statement fails and the writes stay committed.program_boundarynow treats proofs as their own kind of statement. Before anything runs, it rejects withunsupporteda program that writes and has a proof that isn't its last statement, or that ends with a proof and also holds a query (?…or.rule). Each case gets its own error message.revisionfield naming the snapshot it was evaluated on. The core-concepts, explainability and WebSocket API guides now describe pinned proofs, including a new "Proofs of a Guarded Write" section. New handler tests and integration tests (tests/pinned_proof_tests.rs) cover the behavior; a new test hook between staging and commit is used to force a concurrent write.Risk Assessment
.rulefix is correct and complete (Query and RuleQuery are the only statements that set query_to_execute), and only a minor truncation-signal gap remains.Testing
I started a disposable inputlayer-server (temp data dir, local port) and ran the intent's decision flow and each boundary case over the real WebSocket API. - The token-form claim returned per-statement counts and a proof pinned to the pre-commit revision.needandtool_readyappeared as fact leaves and!attemptas a negation leaf. - A repeated claim got zero counts and an empty proof. - The same.whysent on its own afterwards returned nothing, which is the problem this change fixes. - Proof-not-last, query+proof,.rule+proof and.rel+proof were all rejected withunsupported, and nothing was applied. - A failed proof kept its writes. -.why fulland.why_notexplained the state before the write. - A program without writes still runs.whyas before. The concurrency scenario (a restage after a conflicting commit, and writes landing during proof search) can't be timed over the socket. I ran the targeted handler and integration tests that drive it through test hooks, and they passed, but that is not a live result, so the scenario is marked untested. I then stopped the server and deleted the temp data.Evidence: Live WebSocket transcript of all scenarios
Evidence: WebSocket driver script
Evidence: Targeted pinned-proof / boundary test results
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
src/protocol/handler/program_boundary.rs:144- TheQueryWithProofcheck only rejectsStatement::Query..rule <name>(MetaCommand::RuleQuery) has roleLocal, so it passes the boundary check, but the handler turns it into a query (query_to_execute = Some(...), handler.rs:2878-2880). Example:+r(1)\n.rule foo\n.why ?r(X)is accepted, commits, and then takes the pinned-proof early return (handler.rs:3355-3380), so the.rule fooquery never runs and the reply gives no error for it. The rule-query result is lost without any signal. This contradicts the documented contract that a program cannot hold both a query and a proof and is rejected withunsupportedbefore anything runs. Fix: make the check at program_boundary.rs:142-147 also matchStatement::Meta(MetaCommand::RuleQuery(_)), i.e. every statement that setsquery_to_execute..rel <name>is the only other statement that sets it, and it is already rejected asOutside.🔧 Fix applied.
✅ Re-checked - no issues remain.
src/protocol/handler.rs:3318- The pinned-proof reply keeps onlyproof_treesandtiming_breakdownfromexplained. It dropsexplained.truncated, the row cap thatProofSnapshot::whyrecords throughlast_result_truncated(). When a pinned.whymatches more tuples than the result row cap, the reply carries a capped set of proof trees but reportstruncated: false(the default frommessages_result), so the client cannot tell the proof is incomplete. This is unlikely for a single-candidate guard proof, but it can happen with a broad.why ?eligible(S, T). Fix: carryexplained.truncatedinto the returnedQueryResult.✅ **Test** - passed
✅ No issues found.
.why ?candidate(...)wins and its reply carries 4 message rows plus a proof (offer fact, tool_ready fact, !attempt absence, revision).whysent as a separate request after the claim explains nothing because the claim withdrew the row (the problem this change fixes)proof_trees: []unsupportedat its index and nothing is applied?queryin a program that writes and ends with a proof is rejected withunsupportedand nothing is applied.rule <name>query in a program that writes and ends with a proof is rejected withunsupportedand nothing is appliedWhy errorat its index while the program's write stays committed.why_notending a write is evaluated on the snapshot before the insert ('No matching tuples in offer');.why fullending a claim returns a proofBuiltcargo build --bin inputlayer-serverand ran it in a throwaway data dir on 127.0.0.1:18731 with a bootstrap admin passwordDrove the server over WebSocket withnode drive.mjs 18731 <pw> < steps_main.json(decision pack, token-form claim + .why, separate .why after the claim, refused re-claim, proof not last, ?query+proof, .rule+proof, failed proof, .why_not and .why full ending a write, decisions check, a program without writes)node drive.mjs ... < steps_docs.json: ran the explainability.mdx 'Proofs of a Guarded Write' example verbatim on a fresh KGnode race.mjs 18731 <pw>: 8 authenticated connections sent the same guarded claim at the same timeRestarted the server on the same data dir and ransteps_restart.json(decisions persisted; re-claim refused with an empty proof)cargo test --test pinned_proof_testscargo test --lib -- pinned_proof program_boundary✅ No issues found.
Builtcargo build --bin inputlayer-serverand ran it on 127.0.0.1:18917 with a temp data dir and a throwaway admin passwordnode drive.mjs ws://127.0.0.1:18917/ws: sent 20 programs over the live WebSocket API (login, kg create, decision pack, token-form claims, boundary rejections, failed proof, .why full, .why_not, rollback, no-write regression)cargo test --lib -- pinned_proof_tests program_boundary proof_snapshot_tests(includes the Commit/ProofSearch hook tests for a restage and for writes after the commit)cargo test --test pinned_proof_tests(decision with proof, racing decisions, rejection survives restart)✅ **Document** - passed
✅ No issues found.
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ No issues found.
✅ **Push** - passed
✅ No issues found.
✅ No issues found.