Orchestrata Ultimate / Specs / 114-governance-rule-learning
114-governance-rule-learning: Governance Rule Learning
Three capabilities on machinery that already exists:
At a glance
| Item | Value |
|---|---|
| Spec directory | specs/114-governance-rule-learning/ |
| Status line in the spec | Draft |
| Requirement IDs defined | 17 |
With a @traces citation in src/ |
1 (5.9%) |
With a @traces citation in tests/ |
5 (29.4%) |
| Tracked files | 2 |
| RTM page | 114-governance-rule-learning |
Reading this spec
This spec defines how governance rules can be learned from the project's own history without letting a statistical system enforce anything. It describes three capabilities on machinery that already exists. LEARN is a mining pass over the repository's open-vocabulary defect corpora (rulings, adjudications, postmortems and ruling-shaped prompt history) that induces candidate rules as reviewable Prolog clauses with provenance; closed-vocabulary tool output is measured as not inducible and stays a fact source. QUARANTINE and PROMOTE means every induced rule starts as pending, so it reports but never fails a build, and becomes enforcing only through explicit human promotion reviewed by both reviewers. EXPLAIN turns the question "why isn't this feature implemented or this policy mechanized" into a Prolog why-not query using the existing meta-interpreter.
The spec calls itself the disciplined survivor of two refuted designs. An earlier architecture note proposed a CLIPS and Prolog dual-engine rule synthesizer with hot reloading, but the spec records that CLIPS is not a project dependency and is imported nowhere in the repository. Section 4 explains why this design does not reopen those proposals.
The four functional requirement groups map to the 17 requirement IDs (REQ-114-NNN). FR1 (LEARN): the miner draws candidates only from open-vocabulary human and incident corpora, and rejections are recorded with reasons so a rejected candidate is never re-proposed. FR2 (QUARANTINE): pending is a severity value beside error and warning in one severity channel, isolation is by predicate head and module rather than by file, and gate hardening prerequisites include an expected clause-count invariant that returns INVALID on a mismatch. FR3 (PROMOTE): promotion is an explicit human act recorded in the clause header with date, reviewers and provenance, with no automatic path; promoted rules are ordinary rules using ground extractor facts and complete answer sets with no confidence thresholds; machine-authored rule files fall under the content-hash freshness discipline. FR4 (EXPLAIN): answers carry derivations with bound witnesses, and a positive, decomposable requirement_implemented/2 rule is a prerequisite.
The only module that cites the spec is the gate engine. The requirements traceability matrix lists each ID. The spec's own status line reads Draft and it tracks 2 files. Measured coverage is 1 of 17 requirements cited by at least one @traces in src/ (5.9%), spread over 1 module; the module citing the most of them is orchestrata_ultimate/gates/engine (1 requirement). In tests/, 5 requirements are cited by a test-side @traces. 16 requirements have no citing symbol, the largest groups being REQ-114-FR3 (5), REQ-114-FR2 (4) and REQ-114-FR4 (4); the behaviour may still exist in code that was never annotated.
Requirements
Checkbox is the state recorded in the requirements checklist; Traced by is the count of src/ symbols whose @traces names the ID.
| ID | Done | Requirement | Traced by |
|---|---|---|---|
REQ-114-FR1-001 |
no | Miner sources candidates from open-vocabulary human/incident corpora only (rulings canon, ADR-0014 adjudications, postmortems, ruling-shaped prompt history); NEVER from closed-vocabulary tool output (code-review's 39 fixed codes, T1–T20, the gate scripts — measured 0-residual on two disjoint subsets 2026-08-19), which stays gate FACT input per REQ-GATE-010 | none |
REQ-114-FR1-002 |
no | Miner is read-only; output is candidate .pl clauses + per-candidate provenance (source artifact id, incident references, occurrence count, exemplar file:line list where applicable); provenance-less candidates invalid |
none |
REQ-114-FR1-003 |
no | Rejections recorded with reasons; rejected candidates never re-proposed (tombstone dedupe); review outcomes feed the next mining pass | none |
REQ-114-FR2-001 |
no | Induced rules enter status pending; a pending rule never changes a gate verdict or exit code (gate run exits identically with the rule present or absent, finding reported as pending) | none |
REQ-114-FR2-002 |
no | One severity channel: pending is a severity value beside error/warning; blocking/1 stays blocking(error); error / warning-forever / pending-awaiting-review remain three distinguishable states |
none |
REQ-114-FR2-003 |
no | Quarantine rulebase uses a DISTINCT head (candidate_violation/4, never violation/4) AND declares its own :- module with an explicit export list (file separation alone was demonstrated 2026-08-19 to REPLACE the enforcing violation/4 clauses 5→1 on consult); must pass prolog_meta_lint.py before being consulted at all |
none |
REQ-114-FR2-004 |
no | Gate hardening PREREQUISITES (live defenses independent of learning): (a) GateEngine._evaluate asserts an expected violation/4 clause-count invariant and returns INVALID on mismatch (replaces the rules_loaded == 0-only guard under which a 5→1 clobber passes as "1 rule clause(s)"); (b) prolog_meta_lint.py gains a cross-rulebase predicate-redefinition check |
5 |
REQ-114-FR2-005 |
no | Pending-rule store is a version-controlled file or governed-ledger KG artifact, NEVER asserted Prolog state (ADR-0005); factory/promotion/store.py is the reusable promotion-ledger substrate |
none |
REQ-114-FR3-001 |
no | Promotion is an explicit human act reviewed by BOTH reviewers, recorded in the clause header (date, reviewers, provenance); no automatic promotion path (ADR-0014 discipline) | none |
REQ-114-FR3-002 |
no | Promotion mechanism is a committed .pl diff into gate_rules.pl (rewritten onto the enforcing violation/4 head) + a default_severity/2 clause; hot-reloading / runtime rule deployment prohibited |
none |
REQ-114-FR3-003 |
no | Promoted rules are ordinary rules: ground extractor facts only (ADR-0025), complete answer sets / no confidence thresholds (ADR-0026), explainable via explain.pl |
none |
REQ-114-FR3-004 |
no | Demotion/retirement path exists with the same review provenance, feeding the tombstone record | none |
REQ-114-FR3-005 |
no | Machine-authored rule files come under the content-hash freshness discipline (extend .py-only extractor hashing to consulted .pl files, or verify a recorded hash at consult time); until then a KNOWN GAP — quarantine files not consulted alongside enforcing rules outside CI-observed runs |
none |
REQ-114-FR4-001 |
yes | "Why isn't requirement/feature X implemented" answerable as a why-not query over requirement/trace/coverage facts emitted from trace_intel's deterministic computations (never recomputed inside Prolog) — trace_intel why-not <REQ-ID> → tool_why_not_req/2; conjuncts read the same traces/2, spec_requirement/3 and entry_reachable/1 the Layer-3 finders walk |
none |
REQ-114-FR4-002 |
yes | Answers carry derivations with bound witnesses (ADR-0031); model shape = gate uncovered deriving unmechanized long-line policy from tool_rule_status(ruff,'E501',ignored) — verified on the live corpus: REQ-000-004 ⇒ failed_subgoal: req_traced_by_code('REQ-000-004',A) under head: user:requirement_implemented('000','REQ-000-004'), and the rendering is STABLE across repeated identical queries (head_to_atom/2 numbervars a copy_term; bare ~q leaked SWI's internal variable counter, which varied run to run) |
none |
REQ-114-FR4-003 |
yes | Sidekick queries are read-only and freshness-honest: stale fact base ⇒ INVALID-with-names, never a confident answer — sidekick_precondition_report/1 in traceability_db.pl gates tool_why_not_req/2; three-valued (fresh answers, stale_fact_base and unknown_freshness refuse), and trace_intel why-not exits 2 = invalid, matching gate run's own 0 pass / 1 fail / 2 invalid. Built on kb_module_changed/2 (codebase_db.pl) rather than freshness.pl::gate_precondition/1 — see spec.md §FR-004 for why the two live in different engines. Verified live 2026-08-22: editing two source files made the real corpus refuse to answer, naming scripts.trace_intel and tests.prolog.test_requirement_whynot with reason: size, was 12859, now 18160. |
none |
REQ-114-FR4-004 |
yes | PREREQUISITE — a positive, decomposable rule body exists: requirement_implemented/2 in traceability_db.pl (see the AMENDED note in spec.md §FR-004 for why it is sited there and not in spec_completeness.pl, whose implemented/2 remains fact-only and clause-less — pinned by a regression test); acceptance MET — a requirement lacking @traces gets that conjunct named with a witness, proved for all three conjuncts in tests/prolog/test_requirement_whynot.py |
none |
Code that traces this spec
| Module | Symbols | Requirements |
|---|---|---|
orchestrata_ultimate/gates/engine |
5 | 1 |
Checklists
checklists/requirements.md
The requirement list above comes from this file; 4 of 17 checkbox items are ticked.
Full spec text
Verbatim from specs/114-governance-rule-learning/spec.md; headings are pushed down two levels.
SPEC-114: Governance Rule Learning
Metadata
- Spec ID: 114-governance-rule-learning
- Title: Governance Rule Learning — induce rules from open-vocabulary defect corpora, quarantine as pending, promote by dual human review; why-not as the standing sidekick
- Author: Mohamed
- Status: Draft
- Created: 2026-08-19
- Priority: P1
- Version: 0.2.0
- Review: adversarial review 2026-08-19 applied (quarantine isolation demonstrated insufficient with real swipl; freshness exposure corrected in mechanism and polarity; induction corpus re-measured)
- Related: SPEC-007 (Prolog governance), SPEC-033 (build factory), SPEC-113 (measurement retention — the instrument half); ADR-0005, ADR-0014, ADR-0025, ADR-0026, ADR-0027, ADR-0031; RUL-013, RUL-014;
plans/contracts/prolog-gates.contract.json(REQ-GATE-010/011) - Supersedes (jointly with SPEC-113):
docs/architecture/learning.md(unbuilt vision doc — see its status header) - Directive: Mohamed, 2026-08-19 — self-learning from code-review errors, design/code anti-patterns, and lack of spec/traces for code: "the more issues we fall into the prolog is watching and learning and adding rules but they need to be reviewed by us both objectively and then the rules that prolog adds automatically are in status pending until we promote them"; and "why isn't this feature implemented should be in prolog so prolog can be like your sidekick".
1. Overview
What
Three capabilities on machinery that already exists:
- LEARN — a mining pass over the repo's OPEN-vocabulary defect corpora (rulings, adjudications, postmortems, ruling-shaped prompt history) induces CANDIDATE governance rules as reviewable
.plclauses with provenance. Closed-vocabulary tool output is measurably not inducible (§2) and stays a fact source. - QUARANTINE + PROMOTE — every induced rule enters status pending: it reports, it never fails a build. It becomes enforcing only on explicit human promotion, reviewed objectively by both reviewers. Promotion rides the existing severity machinery (
gate_severity.pl), per the 2026-08-19 resolution ofplans/prolog-gates-implementation-plan.mdopen question 1: advisory-vs-blocking is a severity VALUE in ONE channel, never a second channel. ADR-0014 ("Adjudications are promoted into the Project KB", Accepted 2026-07-24) is this promotion model already in ADR form; this spec extends it to rules. - EXPLAIN — "why isn't this feature implemented / this policy mechanized" is a Prolog why-not query. The meta-interpreter exists (
explain.pl, ADR-0031,explain_whynot/2,3); the work is emitting requirement/trace/coverage facts and givingimplemented/2a positive, decomposable definition (§FR-004).
Why
This is the disciplined survivor of two refuted designs. docs/architecture/learning.md proposed a CLIPS+Prolog dual-engine rule synthesizer with hot-reloading — CLIPS has zero entries in pyproject.toml and uv.lock, no clipspy in the venv, and zero imports repo-wide (/usr/bin/clips is a system package invoked by exactly one standalone benchmark script nothing depends on), and the doc's GovernanceRuleLearner has exactly one occurrence in the entire repository: the doc itself (verified 2026-08-19). The statistical-learning premise was separately measured dead by plans/prolog-learning-subsystem-research-2026-08-12.md. What survives both refutations is exactly this spec's shape: deterministic findings in → reviewable rule proposals out → human promotion gate → ordinary rules. Nothing statistical crosses into the rulebase (SPEC-113 FR-006), and nothing enforces without a human decision.
How
The miner follows the established read-only shape of scripts/mine_prompt_history.py (mine → cluster → diff against the ratified canon → report what is new; never writes a ruling). Candidates land in a quarantine rulebase that is isolated by PREDICATE HEAD and MODULE, not merely by file (§FR-002 — file separation alone was demonstrated to destroy enforcing rules, not protect them); promotion is a reviewed .pl diff into the enforcing rulebase — rules-as-reviewable-data, the property the research record names as one of the three reasons the gate layer earns its Prolog. Rejections are recorded with reasons so the miner never re-proposes them — the same discipline as the research record's "explicitly dropped, with numbers attached so they are not re-proposed".
2. The corpus — what is and is not inducible (measured 2026-08-19)
Closed-vocabulary tool output is NOT an induction source. scripts/code-review.py defines 39 fixed literal codes, and every CodeIssue.code is one of those, a ruff id, or the MYPY_ERROR catch-all — there is no open or uncategorized bucket. Measured on two DISJOINT subsets by two independent agents (2026-08-19 adversarial review): core+gates+governance → 740 findings across 11 codes (top-3 = 75.9%, zero ERROR); core+agents → 1,078 findings across 10 codes (top-3 = 72.0%, zero ERROR). Residual findings outside already-encoded checks: 0 of 740 and 0 of 1,078. Inducing rules from that stream can only rediscover the rules that produced it. The same holds for scripts/test-review.py's fixed T1–T20 and the deterministic gate scripts (check_wiring.py, check_spec_authority.py, check_requirement_corpus.py, audit_test_purity.py, gate_parallel_safety.py) — each IS already a mechanized rule. All of these remain what REQ-GATE-010 says they are — FACT sources (catalogue, configured status, run outcome, findings) — never learning input.
The genuinely open corpus is the human/incident channel — defects caught by a person or an incident that are NOT yet mechanized. That is what the directive's "code review errors and anti-patterns" means in the evidence:
| Source | What it contributes | Evidence |
|---|---|---|
Rulings canon (~/workspace/knowledge-wiki/rulings/RUL-*.md) |
ratified corrections awaiting mechanisms — RUL-014 ("every ruling must resolve to a mechanism") makes the miner's job precise: propose the missing mechanism | rulings_lint.py resolution logic |
| Adjudications (ADR-0014) | disputes resolved by the human, promoted into the project KB — the promotion pipeline this spec generalizes to rules | docs/adr/0014-adjudications-promoted-into-project-kb.md |
Postmortems / incident records (plans/*postmortem*.md) |
measured failure classes with named mechanisms | e.g. plans/suite-runtime-failure-postmortem-2026-08-12.md |
| Ruling-shaped prompt history | the PRECEDENT miner: read-only, cluster, diff-vs-canon, report | scripts/mine_prompt_history.py docstring: "never writes a ruling" |
Traceability gaps are large but already deterministic — they feed FR-004, not FR-001. Measured 2026-08-19: orphans 4,093 / untraced_reqs 2,433 / unspecced 321, against 5,531 spec requirements and 7,743 traces. That is thousands of positive labels where the refuted duration-prediction problem had zero — the economics genuinely invert relative to the research record's "no positive labels" finding. But scripts/trace_intel.py already answers those questions deterministically, so the honest form is fact emission plus why-not (REQ-114-FR4-001), not a learner rediscovering what a query already knows.
3. Functional Requirements
FR-001: LEARN — candidate induction from open-vocabulary corpora
- REQ-114-FR1-001: The miner SHALL source candidates from the open-vocabulary human/incident corpora in §2 (rulings canon, ADR-0014 adjudications, postmortems, ruling-shaped prompt history) and SHALL NOT induce from closed-vocabulary tool output (
code-review.py's 39 fixed codes,test-review.py's T1–T20, the deterministic gate scripts): measured 2026-08-19, such streams contain zero residual findings outside their own encoded checks (0/740 and 0/1,078 on disjoint subsets), so induction from them can only rediscover the producing rule. Tool output remains gate FACT input per REQ-GATE-010 ("Prolog does not reimplement a linter"). - REQ-114-FR1-002: The miner SHALL be read-only over its sources (the
mine_prompt_history.pyshape) and its output SHALL be data: candidate.plclauses plus a provenance record per candidate — source artifact (ruling/adjudication/postmortem id), incident references, occurrence count, and exemplarfile:linelist where applicable. A candidate without provenance is invalid. - REQ-114-FR1-003: Rejected candidates SHALL be recorded with the rejection reason, and the miner SHALL NOT re-propose a rejected candidate (tombstone dedupe). Promotion/rejection decisions are themselves inputs to the next mining pass — this is the feedback loop that "improves the inference on errors": the system learns from its own review outcomes, deterministically.
FR-002: QUARANTINE — pending is non-enforcing, and isolation is by head and module, not by file
This is the load-bearing safety property of the whole design, and the naive version of it was demonstrated broken during adversarial review (2026-08-19, real swipl): consulting a second module-less .pl file that defines violation/4 REPLACES the enforcing clauses instead of adding to them — SWI emits only Warning: Redefined static procedure violation/4 and the measured clause count went 5 → 1, leaving ONE pending rule as the entire rulebase. Declaring :- dynamic violation/4. in a first-loaded file does not prevent it (tested as a variant). Production shape confirms the exposure: violation/4 is static with 5 clauses in gate_rules.pl, which declares no :- module; gates/engine.py::_loaded (lines 352–388) consults everything into one process-global janus engine; code_facts.pl's :- dynamic declarations cover fact predicates only, not violation/4. And the gate would have reported a clean PASS while it happened: _evaluate (engine.py lines 248–303) guards only rules_loaded == 0, so 1 surviving clause passes as "1 rule clause(s)". Neither existing safeguard catches it: prolog_meta_lint.py has exactly four checks (check_no_cut :683, check_no_dynamic_goal :762, check_no_rule_side_effects :791, check_stratification :961) — none about redefinition — and tests/prolog/test_module_exports.py covers only files that declare :- module.
- REQ-114-FR2-001: Every induced rule SHALL enter status pending. A pending rule SHALL NOT change any gate verdict or exit code. Acceptance:
scripts/gate.py runon a tree containing a violation of a pending rule exits with the same code as if the rule did not exist, while the finding is reported, labeled pending. - REQ-114-FR2-002: Severity stays ONE channel (open-question-1 resolution, 2026-08-19):
pendingis a severity value alongsideerrorandwarningingate_severity.pl(default_severity/2,severity_exception/3,severity/3), andblocking/1remains exactlyblocking(error)(gate_severity.pl line 80). Three states SHALL be distinguishable and never collapsed: error (enforcing), warning (deliberately advisory, forever, by human decision), pending (learned, awaiting review).pendingis a lifecycle state, not a third permanent tier — it does not reopen the rejected "info" level (gate_severity.pl lines 34–38), because every pending finding has a required next action: review. - REQ-114-FR2-003: The quarantine rulebase SHALL use a DISTINCT PREDICATE HEAD (
candidate_violation/4— neverviolation/4) and SHALL declare its own:- modulewith an explicit export list. Both, not either: the module boundary stops namespace capture, and the distinct head guarantees that even a mis-loaded candidate file cannot redefine the enforcing entry predicate — file separation alone is the clobber vector demonstrated above, not a mitigation. The quarantine file SHALL additionally passscripts/prolog_meta_lint.pybefore it may be consulted at all. - REQ-114-FR2-004: Two gate-hardening changes are PREREQUISITES for consulting any quarantine file, and stand on their own as live defenses independent of learning: (a)
GateEngine._evaluateSHALL assert an expected clause-count invariant forviolation/4— the loaded count equals the count the enforcing rulebase registers — and return INVALID on mismatch, replacing the currentrules_loaded == 0-only guard (engine.py lines 277–289) under which a 5→1 clobber reports a clean PASS as "1 rule clause(s), no violations"; (b)prolog_meta_lint.pySHALL gain a redefinition check: no consulted file may define a predicate head that another consulted rulebase already defines. NOTE: the redefinition hazard exists TODAY with zero learning code — any second module-less rulebase definingviolation/4silently disables the gate. - REQ-114-FR2-005: The pending-rule store SHALL be a version-controlled file (or a KG artifact with a governed ledger), NEVER asserted Prolog state: ADR-0005 constrains the shape — rules live in version control and only GROUND facts are ever
assertz'd.factory/promotion/store.py(namespace-flip promotion with a governed ledger) is the reusable substrate for the pending → promoted transition record.
FR-003: PROMOTE — dual human review, rules-as-reviewable-data
- REQ-114-FR3-001: Promotion SHALL be an explicit human act, reviewed objectively by BOTH reviewers (Mohamed and the orchestrator), and recorded: the promoted clause carries a header comment naming date, reviewers, and the FR1-002 provenance. No automatic promotion path exists — no occurrence-count threshold, no age-out, no auto-escalation. (ADR-0014 established this promotion discipline for adjudications; rules inherit it.)
- REQ-114-FR3-002: The promotion mechanism SHALL be a committed
.pldiff: the clause moves from the quarantine file intogate_rules.pl(rewritten onto the enforcingviolation/4head) and gains adefault_severity/2clause (error, or warning for a deliberately-advisory promotion). Hot-reloading and runtime rule deployment (thedocs/architecture/learning.mdPhase 4 design) are PROHIBITED — a rule change is a code change, reviewed as one. - REQ-114-FR3-003: A promoted rule is an ordinary rule with no special provenance at evaluation time: it consumes only extractor-emitted ground facts (ADR-0025), returns complete answer sets — no confidence thresholds, per ADR-0026's "no truncated answers" — and is explainable by
explain.pllike every other rule. The learning lives entirely in the proposal step; nothing statistical survives promotion. - REQ-114-FR3-004: Demotion SHALL exist: a promoted rule that misfires can be demoted back to pending or retired, recorded with the same review provenance, feeding FR1-003's tombstones.
- REQ-114-FR3-005: Machine-authored rule files (the quarantine file and, on promotion, the clauses' home rulebase) SHALL be brought under the content-hash freshness discipline. Today
staleness/2ranges overcontent_hash(snapshot|disk, File, H)FILE hashes, which the extractor emits for.pyfiles only (gates/fact_extractor.py, hash emission +_undescribed_disk_filesscope, ~line 802), so no consulted.plrulebase carries a content hash —gate_rules.plincluded. A human-authored rule tolerates that (every change is a reviewed diff); for a MACHINE-authored rule nothing would detect divergence between the promoted rule and what actually runs. Either extend extractor hashing to consulted.plfiles or record-and-verify the promoted file's hash at consult time; until one of those lands, this is a KNOWN GAP and quarantine files may not be consulted alongside enforcing rules outside CI-observed runs.
FR-004: EXPLAIN — why-not as the standing sidekick
The machinery exists: explain.pl implements proof trees (explain_call/2) and why-not (explain_whynot/2,3 — every candidate clause's first failing precondition, with bound witnesses via bind_witness/2), exercised by tests/prolog/test_explain.py and wired as gate explain / gate why-not since 2026-08-08 (plan changelog). Coverage inference is specced: REQ-GATE-011's uncovered(Policy) :- policy_requirement(Policy), \+ covered(Policy) (plan line 852), with policy_mechanism/3 declared and emitted (code_facts.pl lines 237–243; FactExtractor._tooling_facts, fact_extractor.py line 869).
-
REQ-114-FR4-001: "Why isn't requirement/feature X implemented?" SHALL be answerable as a why-not query over requirement/trace/code facts: the fact layer gains requirement-coverage facts (requirement exists;
@tracespresent/absent; test citing it present/absent; module implementing it present/absent) sourced from the deterministic computationsscripts/trace_intel.pyalready performs (orphans,untraced-reqs,unspecced— 4,093 / 2,433 / 321 respectively, measured 2026-08-19; re-measured 2026-08-21: 4,484 / 2,248 / 387 against 5,561 spec requirements and 8,668 traces, so the corpus grew while coverage improved) — emitted as facts, never recomputed inside Prolog. -
REQ-114-FR4-002: The answer SHALL carry its derivation (ADR-0031): the why-not output names the first failing precondition with bound witnesses ("REQ-xxx has no
@tracesin any module", "policy P has no enabled tool rule and no native rule"), never a bare no.gate uncoverednaming long-line policy as unmechanized fromtool_rule_status(ruff,'E501',ignored)(contract acceptance[8]) is the model answer shape. -
REQ-114-FR4-003: Sidekick queries SHALL be read-only over the fact base and SHALL respect the freshness contract: a query over a stale fact base reports INVALID-with-names (
freshness.pl::gate_precondition/1), never a confident answer about bytes that no longer exist.AMENDED 2026-08-22 (implementation). The mechanism is
sidekick_precondition_report/1intraceability_db.pl, notfreshness.pl::gate_precondition/1, for the same two-engine reason as FR4-004.gate_precondition/1infers overcontent_hash(snapshot|disk, File, H), whichFactExtractoremits into the GATE engine;trace_intel's session consultscodebase_db.pl+traceability_db.pl+ the two generated fact files and has nocontent_hash/3at all.codebase_db.plalready models the identical question over ITS facts —kb_module_changed/2overfile_meta/5, with the same three-valued discipline it documents as "unknownrather thanfreshwhen nofile_meta" — so the sidekick reuses that rather than duplicating the gate's extraction into a second engine.Three-valued, and the third value is load-bearing at BOTH ends:
stale_fact_basenames every changed module with its reason, andunknown_freshnessfires on an emptyfile_meta/5— because an empty KB has no changed modules and a naive "no changes ⇒ fresh" test would report a KB that was never built as perfectly current. The freshness check precedes even theunknown_requirementbranch: over a stale base, "never heard of that requirement" is itself an untrustworthy claim, since it may have been added tospecs/after the last extract.trace_intel why-notexits 2 = invalid, matchinggate run's existing0 pass / 1 fail / 2 invalid. Collapsing invalid into 0 is the false-greenfreshness.plwas written against; collapsing it into 1 would read as "this requirement failed".Verified live 2026-08-22 — the corpus went stale during this work, on a run that had already auto-refreshed:
outcome : invalid reason : stale_fact_base stale : ['scripts.trace_intel', 'tests.prolog.test_requirement_whynot'] why : {reason: size, was: 12859, now: 18160}That also exposed the limit of
ensure_kb(): it refreshes both generated fact files fromplans/ast-ultimate.json, but nothing refreshes that snapshot from disk, so a source edit leaves the chain internally consistent with an out-of-date snapshot. The remedy the hint names is re-serializing the snapshot, not re-running the query. -
REQ-114-FR4-004: PREREQUISITE — the implementation predicate SHALL be given a positive, decomposable rule body before any sidekick answer is claimed.
spec_completeness.pldeclaresimplemented/2as a bare:- dynamic(implemented/2).and it carries no clauses with bodies —implementation_scanner.py:724buildsimplemented(Spec,Req)fact strings that are asserted at runtime — soexplain_whynot(implemented(S,R), F)returnsF = []: no candidate clauses, no information. Measured against the live engine 2026-08-21:aggregate_all(count, clause(spec_completeness:implemented(_,_),_), N)⇒N = 0.AMENDED 2026-08-21 (implementation). The rule body is sited in
src/orchestrata_ultimate/tools/traceability_db.plasrequirement_implemented/2, not inspec_completeness.pl, because the two live in different engines.spec_completeness.plis consulted byverification/spec_completeness.pyagainst scanner-emitted evidence for ONE spec; that session has notraces/2, noentrypoint/2and no call graph, so a body referencing them would be undefined procedures there and would break the QA volk'sspec_coveragejoin.traceability_db.plis the engine that holds the corpus, so it is the only place a corpus-wide "is this requirement implemented?" is decidable. The body is a pure conjunction of positive goals — the shapeexplain.pldecomposes losslessly:requirement_implemented(SpecId, ReqId) :- spec_requirement(SpecId, ReqId, _), req_traced_by_code(ReqId, _), req_traced_by_test(ReqId, _), req_wired(ReqId, _).The seam is
trace_intel why-not <REQ-ID>(tool_why_not_req/2), notgate why-not— the latter is scoped torule file line, i.e. rule violations at code locations, and has no requirement-shaped query.explain.pljoinedtrace_intel's session (_kb_files()); it was previously consulted only by the gate engine andhistory_intel.Acceptance — MET 2026-08-21, for the untraced requirement
REQ-000-004:head: user:requirement_implemented('000','REQ-000-004') failed_subgoal: req_traced_by_code('REQ-000-004',A)a named conjunct, not an empty list. The free argument renders as a stable
Abecausehead_to_atom/2numbervars acopy_termbefore formatting — bare~qemitted SWI's internal counter (_11094), which varies between identical queries and made the derivation unequal to itself.
4. Why this does not reopen the refuted designs
The five Prolog-risk mechanisms (SPEC-113 FR-006, re-verified 2026-08-19) were stated against statistical facts entering the enforcing rulebase. Under this spec's lifecycle:
- Dissolved by quarantine+promotion (the risk's premise never obtains): confidence thresholds (ADR-0026) — induction outputs a rule proposal, not a weighted fact, and promoted rules carry no scores;
explain.pl's uniform_{kind: fact}leaf (explain.pl line 113) — promoted rules consume only extractor-emitted facts, so every leaf in a proof tree remains snapshot-grounded. - Still live, and mitigated structurally: the flat process-global namespace and NAF non-monotonicity (142 non-comment
\+sites across the 26 rulebases insrc/orchestrata_ultimate/prolog/, 156 including comments, measured 2026-08-19; ADR-0027 recorded 121 on 2026-07-24) — both bite any defective clause that gets consulted, statistical or not. The adversarial demonstration in §FR-002 showed file-level separation makes this WORSE, not better; hence distinct head + module + meta-lint redefinition check + engine clause-count invariant (REQ-114-FR2-003/004). - Corrected during review — the freshness mechanism was inverted. The research record said a learned fact "can never satisfy hash-equality". The measured mechanism is the opposite exposure:
staleness/2ranges over FILE hashes, emitted for.pyonly, so learned facts and.plrule files are INVISIBLE to freshness — never flagged INVALID, never verified. That is a stronger argument for this spec's discipline, and it is why REQ-114-FR3-005 exists.
The quarantine design is not merely compatible with the research record's warnings — it is the mitigation those warnings imply, provided isolation is by predicate head and module (measured: file isolation alone silently destroys the enforcing rulebase), because it moves all learning into the proposal step where nothing the gate trusts can be corrupted.
5. Non-Goals
- Everything in SPEC-113 §4 (the dropped list with numbers): no learned/EWMA prediction, no statistical fact base in Prolog, no risk-ordered test scheduling, no conflict-pair mining below sample adequacy.
- No CLIPS / dual-engine architecture — zero entries in
pyproject.tomlanduv.lock, noclipspyin the venv, zero imports repo-wide;/usr/bin/clipsis a system package used by exactly one standalone benchmark script nothing depends on (verified 2026-08-19). Thelearning.mddual-engine design is retired, not deferred. - No induction from closed-vocabulary tool output (§2) — it can only rediscover the producing rule; measured 0-residual on two disjoint subsets.
- No hot-reloading or monitoring-mode auto-deployment of generated rules — pending rules report through the one severity channel; deployment is a reviewed commit.
- No learning from conversation history into enforcing rules —
mine_prompt_history.pyremains advisory (it names candidate rulings for the human canon; the rulings canon feeds governance separately). - No anomaly detection / predictive rules (
learning.md"Future Extensions") — refuted premise, see SPEC-113 §2's burden-of-proof clause.
6. Traceability
Implementation code SHALL carry @traces to REQ-114-*. Rule-lifecycle artifacts (candidate files, promotion diffs, tombstones, the FR2-005 ledger) are governed by FR-002/FR-003 and reviewable in git history. The superseded vision doc docs/architecture/learning.md carries a status header naming this spec, SPEC-113, and the research record.