Skip to main content

g6-prolog

Use when an answer depends on what a Prolog program derives (rules with recursion, lists, arithmetic or terms with structure) and you are about to state the answers, or that a goal does or does not follow, from your own reading of the program instead of running it.

Source facts

Repository
basicisabc1994/g6solver
Last source activity
October 30, 2026 at 10:47
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
g6-prolog
description
Use when an answer depends on what a Prolog program derives (rules with recursion, lists, arithmetic or terms with structure) and you are about to state the answers, or that a goal does or does not follow, from your own reading of the program instead of running it.
# G6 Prolog Runs a goal against a Prolog program with SWI-Prolog, installed on your machine, out of process: a fresh `swipl` for each call, with no start-up files and no packs, bounded by inference, time and stack limits. The program is read clause by clause and never consulted, so no directive runs; the program and the goal must keep to a restricted syntax (below); and every answer is re-checked in a second, fresh SWI-Prolog process. The restricted syntax is not a security boundary, and neither is SWI-Prolog's sandbox library, which checks the goal as an extra layer. G6 runs SWI-Prolog inside its operating-system boundary where one is ready (bubblewrap on Linux, an AppContainer on Windows); elsewhere SWI-Prolog runs as you. Run programs you do not trust inside your own sandbox, such as a container or a virtual machine. ## When to use it - You are about to say "the program gives these answers" or "this goal follows" about Prolog you wrote or were given. - The rules recurse, build lists or terms, or do arithmetic, where reading by eye misses or invents an answer. - You need every answer the program gives, and to know whether the search finished. - You want to know whether a program reads at all, and which predicates it defines, before you rely on it. ## When not to use it - Facts and rules with no function symbols or arithmetic, where you want each answer's derivation: use `datalog`, which needs no outside program and shows why each answer holds. - Facts with degrees of truth or confidence: use `graph_reasoning`. - Arithmetic, quantifiers or a counterexample over numbers: use `formal`. - A program that reads or writes files, prints, changes the database (`assertz/1`), catches errors, uses constraints (`dif/2`, a constraint library) or calls a goal it builds while running (`call/1`): the restricted syntax refuses it, by design. Run it in your own SWI-Prolog. - A program you do not trust, run where G6 has no boundary ready and outside your own sandbox: nothing contains it. - SWI-Prolog is not installed and the person running the server has not allowed it: say so, and ask them. ## Ops - `query`: the answers to `goal` (one Prolog goal, such as `ancestor(tom, X)`) from `program`, at most `max_solutions` of them (100 by default), each as variable bindings, with `exhausted` (sensitive: runs SWI-Prolog, a program on your machine). - `prove`: whether `goal` is derivable from `program` (`succeeded`), with its first answer (sensitive: runs SWI-Prolog). - `check_program`: SWI-Prolog's read of `program`: the predicates it defines, and each syntax error, directive, refused clause and goal outside the restricted syntax with its line and column; nothing is queried (sensitive: runs SWI-Prolog). - `swipl_version`: which SWI-Prolog would run a program, from `swipl --version` (sensitive: runs SWI-Prolog). ## The restricted syntax - No directives (`:- ...`, `?- ...`). Grammar rules (`-->`) are read as the clauses they translate to. - A program defines only its own predicates: no clause for a built-in, a library predicate (`append/3`, `member/2`: call yours `my_append/3`, or use the library's) or a hook SWI-Prolog calls (`portray/1`, `message_hook/3`), and no `Module:Head`. - Every goal, in a clause body and in `goal`, calls a predicate the program defines or one on the allow-list (`allowed/1` in the package's `driver.pl`): `true`, `fail`, `false`, `!`; `=`, `\=`, `==`, `\==`, `@<`, `@>`, `@=<`, `@>=`, `compare/3`; `is`, `=:=`, `=\=`, `<`, `>`, `=<`, `>=`, `between/3`, `succ/2`, `plus/3`; `var`, `nonvar`, `atom`, `number`, `integer`, `float`, `atomic`, `compound`, `callable`, `is_list`, `ground`; `functor/3`, `arg/3`, `=..`, `copy_term/2`; `atom_codes/2`, `atom_chars/2`, `char_code/2`, `atom_number/2`, `number_codes/2`, `number_chars/2`, `atom_length/2`; `member/2`, `memberchk/2`, `append/3`, `length/2`, `nth0/3`, `nth1/3`, `last/2`, `reverse/2`, `msort/2`, `sort/2`, `sort/4`, `sum_list/2`, `max_list/2`, `min_list/2`, `numlist/3`, `list_to_set/2`. - The control constructs (`,`, `;`, `->`, `*->`, `\+`) and `findall/3`, `bagof/3`, `setof/3` (with `^`), `forall/2` and `aggregate_all/3` (`count`, `sum`, `max`, `min`, `bag`, `set`) have their goal arguments checked the same way. `maplist/2` to `maplist/4` are allowed when the closure names a predicate (`maplist(double, L, M)`), which is checked with its added arguments; `call/N`, `foldl` and the other meta predicates are left out. - Never a variable goal, the goal argument of `findall/3` and the others included, and never `Module:Goal`. ## Reading the result `answers` lists each answer's `bindings` (each named variable of the goal and its value as Prolog text; a variable left free is `_V1`, `_V2` and so on) and `text`, the whole answer as the re-check read it. `exhausted` is true only when the search finished below `max_solutions`. `recheck` holds the second process's verdict on each answer and, when the search finished, on its end (index -1). `problems` hold what was refused or did not read, with `line` and `column` (counted from 0, as SWI-Prolog counts). `time_limit` is at most 600 seconds; `inference_limit` is 10,000,000 by default; `memory_mb` is 2048 by default. - `qualified-draft` (on `warning_card.code`; the result's own `code` is empty): `G6_E_PROLOG_ANSWERS`, `G6_E_PROLOG_NO_ANSWERS`, `G6_E_PROLOG_GOAL_DERIVABLE`, `G6_E_PROLOG_GOAL_NOT_DERIVABLE`, `G6_E_PROLOG_PROGRAM_READ` and `G6_E_PROLOG_VERSION_READ`. Nothing here is `verified`. - `blocked-escalated`: `G6_E_PROLOG_GOAL_NOT_ALLOWED` (a goal outside the restricted syntax; the reason names the predicate and where it appears), `G6_E_PROLOG_UNDEFINED_PREDICATE`, `G6_E_PROLOG_DIRECTIVE_REFUSED`, `G6_E_PROLOG_CLAUSE_REFUSED`, `G6_E_PROLOG_SYNTAX_ERROR`, `G6_E_PROLOG_UNSAFE_GOAL` (SWI-Prolog's sandbox library refused a goal the restricted syntax allowed), `G6_E_PROLOG_GOAL_ERROR`, `G6_E_PROLOG_INFERENCE_LIMIT`, `G6_E_PROLOG_TIME_LIMIT` and `G6_E_PROLOG_MEMORY_LIMIT` (the answers found before the limit are kept, not re-checked), `G6_E_PROLOG_CERTIFICATE_REFUTED` (the re-check disagreed), `G6_E_PROLOG_RECHECK_UNFINISHED` and `G6_E_PROLOG_SWIPL_MISSING`. ## Alone and composed Alone, from Python: `from g6.prolog import query, prove, check_program` (or `get_registry().invoke("prolog", "query", {...})`). Over MCP: `g6_invoke("prolog", "query", params_json)`, which runs only when the person running the server allows it; `ops` and `help` answer anywhere. Composed: - Run `check_program` first on a program you did not write, then `query` or `prove`. - When the same rules fit Datalog, run them through `datalog` too: two engines that agree are worth more than one. - Give `coding_agent_gate`'s `verify_answer` the reply as an evidence item of kind `log` before reporting a conclusion that rests on it (it counts as caller-declared). ## Failure modes it catches - `unsupported_conclusion` (a conclusion the rules and facts do not support): `prove` answers from the program as SWI-Prolog runs it, not from a reading of it, and each answer is re-checked: put back into the goal, it must hold again as given, its free variables left free, in a second, fresh process, or the reply is `G6_E_PROLOG_CERTIFICATE_REFUTED`. A goal that fails comes back not derivable, and the failure is re-checked too. - `missed_implication` (a consequence the rules imply that goes unnoticed): `query` collects every answer the program gives up to `max_solutions`, and `exhausted` says whether the search finished; when it did, the search is run again in the second process with a cap one higher and must give the same answers in the same order and no more, so a cut-short list is never passed off as complete. ## Blind spots - Not a security boundary: the restricted syntax and SWI-Prolog's sandbox library check what a program calls. Where G6 has no operating-system boundary ready, SWI-Prolog runs as you, and containing a program you do not trust is your own sandbox's job (a container or a virtual machine). - Not derivable means not derivable from this program under Prolog's closed world, not false: a fact or rule the program leaves out makes a true goal fail. - The answers are what your program says as SWI-Prolog runs it, depth first. A wrong fact or rule gives a wrong answer that passes every check, and a left-recursive rule loops until a limit stops it. - The restricted syntax refuses whole classes of harmless programs: anything that prints, reads, asserts, catches, uses constraints or calls a goal built while running; a predicate named like a library one must be renamed. - No directives at all: no operator, flag, `dynamic`, `table` or module declarations. - An answer that is a cyclic term is refused (`G6_E_PROLOG_GOAL_ERROR`), not reported. - Memory: SWI-Prolog's stacks are capped on every system at half of `memory_mb` (at least 64 MB); the whole process is capped only on Windows. Inference, time and stack limits stop long or large searches. ## What it needs - SWI-Prolog, an outside program G6 never installs (BSD-2-Clause). Install it from swi-prolog.org, which puts `swipl` on PATH, or set `G6_SWIPL` to the `swipl` program. No pip extra: caller code never runs in G6's process. - Over MCP all four ops run only when the person running the server lists them (or `prolog`) in `G6_MCP_SENSITIVE`; an agent never edits that setting to grant itself one. ## Examples ```g6-example op=query {"program": "parent(tom, bob).\nparent(bob, ann).\nancestor(X, Y) :- parent(X, Y).\nancestor(X, Z) :- parent(X, Y), ancestor(Y, Z).\n", "goal": "ancestor(tom, Who)"} ``` With SWI-Prolog installed and the op allowed, the reply is `qualified-draft` with `G6_E_PROLOG_ANSWERS`: `Who` is `bob`, then `ann`, `exhausted` is true and `recheck.passed` is true. ```g6-example op=prove {"program": "parent(tom, bob).\nparent(bob, ann).\n", "goal": "parent(ann, tom)"} ``` `qualified-draft` with `G6_E_PROLOG_GOAL_NOT_DERIVABLE` and `succeeded` false: not derivable from this program, which is not the same as false. ```g6-example op=query {"program": "", "goal": "shell(ls)"} ``` `blocked-escalated` with `G6_E_PROLOG_GOAL_NOT_ALLOWED`: `shell/1` is not on the allow-list, so the goal never runs. ```python from g6.prolog import query found = query("parent(tom, bob).", "parent(tom, Who)") if found.code == "G6_E_PROLOG_SWIPL_MISSING": # SWI-Prolog is not installed here assert "swi-prolog.org" in found.warning_card["suggested_action"] else: assert found.answers[0]["bindings"] == {"Who": "bob"} and found.completion_state == "qualified-draft" assert query("", "shell(ls)").code == "G6_E_PROLOG_GOAL_NOT_ALLOWED" assert query("p(1).", "p(X)", time_limit=601).code == "G6_E_PROLOG_TIME_LIMIT_TOO_LONG" ``` ## The four open problems - **Learning signals.** Not yet: it writes nothing to a run ledger itself. Each catch is a labelled result with its own `G6_E_PROLOG_*` code, SWI-Prolog's messages in `problems` or `reason` and the re-check in `recheck`; recording it is the caller's step today. - **Evaluators the model cannot influence.** The answers are SWI-Prolog's, from a driver G6 writes, and the re-check is a second SWI-Prolog process; no model call runs anywhere in the package. The caller writes the program and the goal, never the check. - **Transfer by failure mode.** The re-check belongs to Prolog, not to a task: any answer is put back into its goal the same way. Not yet: its catch rate on task families it was not built on has not been measured. - **Enough held-out cases.** Not yet: its own catch rate and false-alarm rate are not judged by a sequential test. A repeated workflow that uses it can be tuned with `g6.metaharness`, whose gate judges a change on held-out cases.
View on GitHub