Add Prolog-owned automatic skill activation #122

Closed
lost-rob0t wants to merge 60 commits from feature/117-prolog-skill-activation into main
lost-rob0t commented 2026-08-19 04:44:49 +00:00 (Migrated from github.com)

Closes #117.

Runtime invariant

SKILL.md is confined inert package data. It normalizes into the existing
prompt_unit{unit:skill(...)} IR; the one rlm_prompt_compiler owns routing,
dependency closure, conflicts, packing, explanations, and fingerprints. Skill
content never grants capabilities, authority, handlers, or execution rights.

What changed

  • adds bounded standard-compatible SKILL.md discovery and a closed
    metadata.prolog-rlm JSON adapter;
  • preserves Claude disable-model-invocation compatibility without treating
    package metadata as host policy;
  • rejects descendant symlinks, path traversal, changed admitted bodies and
    resources, oversized files/catalogs, excessive depth, and excessive visited
    entries;
  • indexes bodies/resources progressively and reads only selected skill bodies;
  • removes the competing skill_compile/4 selector, prompt renderer, and
    completion predicate wrapper from the old branch;
  • adds generic host-owned activation:always to rlm_prompt_compiler, outside
    candidate limits and resistant to user-text negation;
  • makes always/always conflicts and supersession structural configuration
    errors while preserving trusted host denial;
  • adds four concise default RLM operating skills: rlm-operate, rlm-recurse,
    rlm-facts, and rlm-constraints;
  • compiles those skills into one bounded system message on the exact root
    planner request, reused without duplication across planner retries;
  • supports trusted global/per-skill disable and explicit selection, failing
    closed on unknown or rejected explicit skills;
  • keeps raw standalone llm_query/3 unchanged;
  • retains the pinned Matt Pocock collection as an optional inert third-party
    catalog rather than ambient core behavior.

Host policy boundary

Only trusted host options may assign activation(always), mandatory context,
provider visibility, availability overrides, or capability requirements.
Natural-language instructions and package-authored metadata cannot pin, unpin,
authorize, or execute a skill.

Verification

  • focused prompt compiler: 33/33;
  • focused skill loader: 30/30;
  • symlink confinement: 2/2;
  • focused completion provider projection: 40/40;
  • planner tool-schema visibility regression: 1/1;
  • focused subagent budget contracts: 12/12;
  • complete deterministic corpus: 867/867 across 78 suites;
  • runtime/load-all checks;
  • deterministic benchmark and depth experiment;
  • CLI JSON demo smoke;
  • deterministic runner integrity and main-ownership probes;
  • copied SWI pack install/load, including all four default skills;
  • git diff --check.

GitHub Actions on exact head 7840e7a8a9f1dc784eb9592f572133a94154cc8a
is the authoritative merge gate.

Non-goals and follow-up

  • #176 retains unified tool/MCP provider projection and projection
    observability;
  • #176/#183 retain broader RLM-internal leaf/repair request propagation and
    compaction reconstitution;
  • #183 retains behavioral live-model evaluation beyond deterministic exact
    request evidence.
Closes #117. ## Runtime invariant `SKILL.md` is confined inert package data. It normalizes into the existing `prompt_unit{unit:skill(...)}` IR; the one `rlm_prompt_compiler` owns routing, dependency closure, conflicts, packing, explanations, and fingerprints. Skill content never grants capabilities, authority, handlers, or execution rights. ## What changed - adds bounded standard-compatible `SKILL.md` discovery and a closed `metadata.prolog-rlm` JSON adapter; - preserves Claude `disable-model-invocation` compatibility without treating package metadata as host policy; - rejects descendant symlinks, path traversal, changed admitted bodies and resources, oversized files/catalogs, excessive depth, and excessive visited entries; - indexes bodies/resources progressively and reads only selected skill bodies; - removes the competing `skill_compile/4` selector, prompt renderer, and completion predicate wrapper from the old branch; - adds generic host-owned `activation:always` to `rlm_prompt_compiler`, outside candidate limits and resistant to user-text negation; - makes always/always conflicts and supersession structural configuration errors while preserving trusted host denial; - adds four concise default RLM operating skills: `rlm-operate`, `rlm-recurse`, `rlm-facts`, and `rlm-constraints`; - compiles those skills into one bounded system message on the exact root planner request, reused without duplication across planner retries; - supports trusted global/per-skill disable and explicit selection, failing closed on unknown or rejected explicit skills; - keeps raw standalone `llm_query/3` unchanged; - retains the pinned Matt Pocock collection as an optional inert third-party catalog rather than ambient core behavior. ## Host policy boundary Only trusted host options may assign `activation(always)`, mandatory context, provider visibility, availability overrides, or capability requirements. Natural-language instructions and package-authored metadata cannot pin, unpin, authorize, or execute a skill. ## Verification - focused prompt compiler: 33/33; - focused skill loader: 30/30; - symlink confinement: 2/2; - focused completion provider projection: 40/40; - planner tool-schema visibility regression: 1/1; - focused subagent budget contracts: 12/12; - complete deterministic corpus: 867/867 across 78 suites; - runtime/load-all checks; - deterministic benchmark and depth experiment; - CLI JSON demo smoke; - deterministic runner integrity and main-ownership probes; - copied SWI pack install/load, including all four default skills; - `git diff --check`. GitHub Actions on exact head `7840e7a8a9f1dc784eb9592f572133a94154cc8a` is the authoritative merge gate. ## Non-goals and follow-up - #176 retains unified tool/MCP provider projection and projection observability; - #176/#183 retain broader RLM-internal leaf/repair request propagation and compaction reconstitution; - #183 retains behavioral live-model evaluation beyond deterministic exact request evidence.
lost-rob0t commented 2026-08-19 04:51:54 +00:00 (Migrated from github.com)

Backlog/security review blocker: rlm_skill currently relies on absolute_file_name/3 followed by lexical path_within/2 prefix checks in catalog traversal and skill_read_resource/3. SWI-Prolog documents that absolute_file_name/3 does not resolve symbolic links, so a symlink below a configured skill root can point outside the root while retaining an in-root lexical pathname. That violates #117's acceptance criterion that skill-relative resources cannot escape the configured root.

Please fix this by validating physical path ancestry or, more conservatively, rejecting symlink components below the trusted configured root. Add adversarial tests for both: (1) a skill-directory symlink to outside the root is rejected during catalog scan; (2) a selected skill resource reached through an in-root symlinked subdirectory cannot read an outside file. Do not weaken confinement to make the tests pass. The configured root itself may remain a trusted explicit host path; the boundary that must be enforced is its descendants.

Backlog/security review blocker: `rlm_skill` currently relies on `absolute_file_name/3` followed by lexical `path_within/2` prefix checks in catalog traversal and `skill_read_resource/3`. SWI-Prolog documents that `absolute_file_name/3` does **not** resolve symbolic links, so a symlink below a configured skill root can point outside the root while retaining an in-root lexical pathname. That violates #117's acceptance criterion that skill-relative resources cannot escape the configured root. Please fix this by validating physical path ancestry or, more conservatively, rejecting symlink components below the trusted configured root. Add adversarial tests for both: (1) a skill-directory symlink to outside the root is rejected during catalog scan; (2) a selected skill resource reached through an in-root symlinked subdirectory cannot read an outside file. Do not weaken confinement to make the tests pass. The configured root itself may remain a trusted explicit host path; the boundary that must be enforced is its descendants.
lost-rob0t commented 2026-08-19 04:54:54 +00:00 (Migrated from github.com)

Scope blocker before ready/merge: this PR currently claims the pinned Matt Pocock skill collection, but the tracked third-party corpus only contains engineering/tdd and engineering/diagnosing-bugs (plus their resources). #117 and the originating request require the collection to be embedded/pinned, not merely two examples. Vendor the stable engineering/, misc/, and productivity/ trees from pinned upstream 9c9f36ccd3995266cd675468af71639c8dde1ec5; keep deprecated/ and in-progress/ excluded from the default corpus. Preserve upstream files byte-for-byte where practical and keep dependency/alias semantics in the trusted Prolog overlay rather than rewriting third-party instructions. Also add the requested explicit thanks/attribution to the repository README.md (UPSTREAM.md alone does not satisfy “thanks to the creator in README”). Update docs/prolog-agent-roadmap.md if this changes PrologAgent readiness per AGENTS.md. Do not mark ready while these are missing.

Scope blocker before ready/merge: this PR currently claims the pinned Matt Pocock **skill collection**, but the tracked third-party corpus only contains `engineering/tdd` and `engineering/diagnosing-bugs` (plus their resources). #117 and the originating request require the collection to be embedded/pinned, not merely two examples. Vendor the stable `engineering/`, `misc/`, and `productivity/` trees from pinned upstream `9c9f36ccd3995266cd675468af71639c8dde1ec5`; keep `deprecated/` and `in-progress/` excluded from the default corpus. Preserve upstream files byte-for-byte where practical and keep dependency/alias semantics in the trusted Prolog overlay rather than rewriting third-party instructions. Also add the requested explicit thanks/attribution to the repository `README.md` (UPSTREAM.md alone does not satisfy “thanks to the creator in README”). Update `docs/prolog-agent-roadmap.md` if this changes PrologAgent readiness per AGENTS.md. Do not mark ready while these are missing.
lost-rob0t commented 2026-08-19 04:55:51 +00:00 (Migrated from github.com)

One acceptance regression to add while this is still draft: prove the opt-out/empty-catalog path. A public rlm:rlm_completion/4 call with skill_catalog(none) (and ideally skill_mode(off)) should reach the planner with the caller's existing planner_instruction unchanged and with no skill body/catalog text injected. #117 explicitly requires compatibility when no skill catalog is configured; right now the tests cover positive injection but not the negative compatibility path.

One acceptance regression to add while this is still draft: prove the opt-out/empty-catalog path. A public `rlm:rlm_completion/4` call with `skill_catalog(none)` (and ideally `skill_mode(off)`) should reach the planner with the caller's existing `planner_instruction` unchanged and with no skill body/catalog text injected. #117 explicitly requires compatibility when no skill catalog is configured; right now the tests cover positive injection but not the negative compatibility path.
lost-rob0t commented 2026-08-19 04:57:31 +00:00 (Migrated from github.com)

Resource-semantics blocker while vendoring the real corpus: skill_resource_metadata/3 currently only admits direct .md/.txt sidecars. That breaks pinned upstream skills which explicitly depend on non-Markdown and/or nested resources: wizard/SKILL.md says to copy template.sh, and diagnosing-bugs/SKILL.md references scripts/hitl-loop.template.sh. #117 says selected skill bodies/resources are loaded after Prolog selects the skill, so the selected instruction bundle must preserve these referenced resources without executing them. Please make resource discovery recursively include bounded, regular, non-symlink text resources under the selected skill directory (at minimum the pinned .sh templates), account them in the skill token ceiling, and render/read them as inert text only. Keep executable authority exactly where it is; loading script text is not permission to run it. Add a fixture/test proving a nested .sh resource is available only after selection.

Resource-semantics blocker while vendoring the real corpus: `skill_resource_metadata/3` currently only admits direct `.md`/`.txt` sidecars. That breaks pinned upstream skills which explicitly depend on non-Markdown and/or nested resources: `wizard/SKILL.md` says to copy `template.sh`, and `diagnosing-bugs/SKILL.md` references `scripts/hitl-loop.template.sh`. #117 says selected skill bodies/resources are loaded after Prolog selects the skill, so the selected instruction bundle must preserve these referenced resources without executing them. Please make resource discovery recursively include bounded, regular, non-symlink text resources under the selected skill directory (at minimum the pinned `.sh` templates), account them in the skill token ceiling, and render/read them as inert text only. Keep executable authority exactly where it is; loading script text is not permission to run it. Add a fixture/test proving a nested `.sh` resource is available only after selection.
lost-rob0t commented 2026-08-19 05:00:16 +00:00 (Migrated from github.com)

Remaining acceptance gates at head 1fe0e905: (1) confinement implementation still uses lexical absolute_file_name + prefix checks despite the new symlink tests; (2) resources are still direct .md/.txt only, so wizard/template.sh and nested diagnosing-bugs scripts are not compiled; (3) skill_catalog(none) compatibility regression is still absent; (4) vendored default corpus is still a subset of the pinned stable engineering/misc/productivity collection; (5) README thanks is now present, but the PrologAgent roadmap still needs the readiness update required by AGENTS.md. Please finish these before waiting on CI/marking ready. The automatic default-corpus activation test added in 1fe0e905 is good.

Remaining acceptance gates at head `1fe0e905`: (1) confinement implementation still uses lexical `absolute_file_name` + prefix checks despite the new symlink tests; (2) resources are still direct `.md`/`.txt` only, so `wizard/template.sh` and nested diagnosing-bugs scripts are not compiled; (3) `skill_catalog(none)` compatibility regression is still absent; (4) vendored default corpus is still a subset of the pinned stable engineering/misc/productivity collection; (5) README thanks is now present, but the PrologAgent roadmap still needs the readiness update required by AGENTS.md. Please finish these before waiting on CI/marking ready. The automatic default-corpus activation test added in `1fe0e905` is good.
lost-rob0t commented 2026-08-25 00:15:06 +00:00 (Migrated from github.com)

ADARD architecture update: #183 reconciles this PR with #173/current main. Preserve the valuable SKILL.md discovery/normalization, confinement, lazy-resource, pinned-corpus and compatibility work, but the separate skill_compile/4 lexical selector + independent count/token/dependency/fingerprint path should not become a second provider-context compiler. Target flow is SKILL.md -> normalized skill -> prompt_unit(skill(...)) -> rlm_prompt_compiler. The default RLM operating skills are host-pinned (activation:always + mandatory context), toggleable by trusted host config, and must be proven in the exact provider-bound request. No code changes were made in this ADARD slice.

ADARD architecture update: #183 reconciles this PR with #173/current main. Preserve the valuable SKILL.md discovery/normalization, confinement, lazy-resource, pinned-corpus and compatibility work, but the separate `skill_compile/4` lexical selector + independent count/token/dependency/fingerprint path should not become a second provider-context compiler. Target flow is `SKILL.md -> normalized skill -> prompt_unit(skill(...)) -> rlm_prompt_compiler`. The default RLM operating skills are host-pinned (`activation:always` + mandatory context), toggleable by trusted host config, and must be proven in the exact provider-bound request. No code changes were made in this ADARD slice.
lost-rob0t commented 2026-08-25 00:18:10 +00:00 (Migrated from github.com)

ADARD/ARARD reconciliation gate

Authoritative design for this PR is now #183: https://github.com/lost-rob0t/prolog-rlm/issues/183

Reconcile this branch against that decision before further implementation/merge work. In particular:

  • keep the useful SKILL.md loader/normalizer, package/resource confinement, compatibility adapters, pinned corpus, provenance, and lazy-resource work;
  • do not preserve skill_compile/4 as a competing provider-context router/scorer/packer;
  • normalize loaded skills into the existing prompt_unit{unit:skill(...)} IR and route selection through the one rlm_prompt_compiler, per #173/#183;
  • consume generic host-owned activation:always semantics for permanent RLM skills rather than creating a skill-only permanence path;
  • preserve the global opt-out and support trusted per-skill disabling without allowing skill-authored/user prose to self-promote or unpin permanent context;
  • provider-bound tests must capture the exact model request and prove default RLM operating instructions are actually present; compiler-internal selection alone is not acceptance evidence.

Workflow gate

We are returning to normal ARARD flow. Auto-RAGE is not the default workflow. RAGE is permitted only after the relevant design has been explicitly approved.

For this PR, #183 is now the approved design gate. Any RAGE/implementation loop must remain within that approved architecture; design changes go back through ARARD/approval first.

## ADARD/ARARD reconciliation gate Authoritative design for this PR is now **#183**: https://github.com/lost-rob0t/prolog-rlm/issues/183 Reconcile this branch against that decision before further implementation/merge work. In particular: - keep the useful `SKILL.md` loader/normalizer, package/resource confinement, compatibility adapters, pinned corpus, provenance, and lazy-resource work; - do **not** preserve `skill_compile/4` as a competing provider-context router/scorer/packer; - normalize loaded skills into the existing `prompt_unit{unit:skill(...)}` IR and route selection through the one `rlm_prompt_compiler`, per #173/#183; - consume generic host-owned `activation:always` semantics for permanent RLM skills rather than creating a skill-only permanence path; - preserve the global opt-out and support trusted per-skill disabling without allowing skill-authored/user prose to self-promote or unpin permanent context; - provider-bound tests must capture the **exact model request** and prove default RLM operating instructions are actually present; compiler-internal selection alone is not acceptance evidence. ### Workflow gate We are returning to normal **ARARD** flow. **Auto-RAGE is not the default workflow. RAGE is permitted only after the relevant design has been explicitly approved.** For this PR, #183 is now the approved design gate. Any RAGE/implementation loop must remain within that approved architecture; design changes go back through ARARD/approval first.
lost-rob0t commented 2026-08-25 06:39:42 +00:00 (Migrated from github.com)

Superseded by #197. PR #197 carried the identical reviewed tree from 7840e7a8a9f1dc784eb9592f572133a94154cc8a on a linear branch and merged via the repository-required rebase method as b82b975d9ba86c4bd8443aaafe43d15af4cfe88c. PR #122 remains open as requested.

Superseded by #197. PR #197 carried the identical reviewed tree from `7840e7a8a9f1dc784eb9592f572133a94154cc8a` on a linear branch and merged via the repository-required rebase method as `b82b975d9ba86c4bd8443aaafe43d15af4cfe88c`. PR #122 remains open as requested.

Pull request closed

Sign in to join this conversation.
No description provided.