Skip to content

feat(genesis): skill extraction and read-only investigation - #192

Merged
Birfy merged 1 commit into
Birfy:mainfrom
HugoXK:feat/genesis-skills-investigator
Sep 24, 2026
Merged

Birfy merged 1 commit into
Birfy:mainfrom
HugoXK:feat/genesis-skills-investigator

Conversation

@HugoXK

@HugoXK HugoXK commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

Genesis: skill extraction and read-only investigation -- the loops that were missing

The problem

The port's own docs name both gaps.

"Skills are inherited but never extracted" (docs/algo-genesis.md). The read half existed: LocalWorld.skills() collects .agents/skills/ along the node chain (hierarchical_skill_names/2), and the brief lists the names. The write half did not; no accepted contribution ever produced a skill file, and the domain shipped one hand-written skill so the inheritance path would be "live rather than decorative" -- which it was not, because this port's agents have no skill_read tool, and a name line telling them to "read one before using it" is an instruction none of them can follow.

The other gap is not about the paper. _MANAGER_PROMPT carries the CONTEXT.md chain and a listing of the files at a node, never their contents -- this port's agents have no read tool. A manager that must decide from the code decides blind, and there was no mechanism to read a node and leave a note about it.

Both gaps share a root cause: one proposal is one completion, not a session. An agent reads nothing the brief does not contain.

What this PR adds

Both features are new modules with no imported engine changes.

--extract-skills: SkillExtractor (examples/genesis/_skills.py)

The reference skill_extractor role (agents/skill_extractor.ex) runs after the PR merges. On this engine a post-merge version is one no later episode of the same run ever sees, so a literal trigger produces the write half and the loop is still open. The port's adaptation runs extraction at the root episode boundary, on the contribution the episode just accepted, so the skill rides in the same proposal and the next agent inherits it -- the loop closes inside one run.

The other adaptation is the reason the loop was never actually live: LocalWorld.skill_bodies renders inherited skill bodies into the brief alongside the names, bounded per-file. Without this the feature is decorative -- the reference puts names in the brief and fetches a body with skill_read, this port's agents have no such tool, and "read one before using it" is an instruction none of them follow.

Skills are bounded separately from work (max_skill_edits=2 beside max_edits=4, against the strategy's max_files_per_diff=6): a lesson trimmed to make room for the file it distilled is self-defeating. The frontmatter (name, description, parameters) is rendered here, not emitted by the model -- the format is the tool's. Counters distinguish the four outcomes that look identical in the artifact: written, duplicate, refused, unparsed.

22 tests in tests/test_genesis_skills.py.

--investigate: Investigator (examples/genesis/_investigator.py)

A manager marks a delegation "investigate": true; the delegation runs Investigator (agents/investigator.ex's read-only half) instead of the recursion, and what it reads at the node is recorded under ## Findings in that node's CONTEXT.md and enters the version. The next agent's brief carries the updated record.

The fan-out to subagent_investigator is deliberately not ported -- that is what a manager with read tools of its own does, and giving the manager read tools is the session work. The one write an investigator may make is the record at the node it was sent to; source edits and writes to an ancestor's record are refused and counted. A read-only delegation with no investigator installed falls through to ordinary work rather than being dropped silently.

14 tests in tests/test_genesis_investigator.py.

Backward compatibility

  • skills=None and investigator=None on RecursiveDelegation are unchanged and default.
  • The existing Delegation(path, objective) signature is unchanged (readonly=False).
  • llm_manager parses "investigate": true when present; a reply that omits it works as before.
  • situate with skill_bodies=False (the default) renders only names, as before.
  • Both flags require --model and print an error and exit without one.

Verification

Item Result
Skill extraction tests 22 passed
Investigator tests 14 passed
Existing genesis tests 121 passed, unchanged
Full suite (deselected 2 pre-existing env failures) 3010 passed, exit 0
Offline domain (minilang, 12 episodes serial) reward 1.000 unchanged

Files

10 files changed, +1307, -30

New:

  • examples/genesis/_skills.py (+295) -- SkillExtractor, prompt, dedup, frontmatter rendering
  • examples/genesis/_investigator.py (+187) -- Investigator, prompt, findings persistence
  • tests/test_genesis_skills.py (+316) -- 22 offline deterministic tests
  • tests/test_genesis_investigator.py (+205) -- 14 offline deterministic tests

Modified:

  • examples/genesis/_delegation.py (+142, -8) -- Delegation.readonly, skills=/investigator= fields, _distil, _investigate, _bound skill bucket, counters
  • examples/genesis/_world.py (+28, -7) -- LocalWorld.skill_bodies field, body rendering in situate, delegation propagation
  • examples/genesis/_suite.py (+8, -2) -- llm_manager parses "investigate": true; prompt rule
  • examples/genesis/genesis_recursive_worlds.py (+47, -2) -- --extract-skills, --investigate flags, wiring, summary lines
  • docs/algo-genesis.md (+32, -11) -- skill-extraction bullet replaces "never extracted"; investigator capability under "two roles"
  • CHANGELOG.md (+47)

…sing loops

SkillExtractor (--extract-skills):
At the root episode boundary, distil an accepted contribution into a
.agents/skills/ file that the rest of the run inherits. The reference role
fires after a PR merges; on this engine a post-merge version is one no
later episode of the same run ever sees, so the literal trigger produces
a write half that nobody inherits. Running inside the episode puts the
skill in the proposal the next episode branches from.

Also carries skill bodies in the brief (LocalWorld.skill_bodies), because
the reference puts names and relies on skill_read, and this port has no
read tools. Without the bodies "read one before using it" is an instruction
none of the agents can follow and the loop is decorative.

Investigator (--investigate):
A manager may mark a delegation read-only (Delegation.readonly). Instead
of the recursion the delegation runs an Investigator that reads the node
files and records what it finds under ## Findings in that node CONTEXT.md,
which enters the version. The fan-out to subagent_investigator is
deliberately not ported — that is what a manager with read tools of its
own is for, and giving the manager read tools is the session work.

Both off by default (skills=None, investigator=None), zero behaviour
change. 36 new offline tests. Full suite exit 0.

@Birfy Birfy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the skill extraction and investigator flow, scope checks, documentation, and tests. The new behavior is opt-in; all 36 focused tests pass locally and CI passes on Python 3.9/3.11/3.12. Approved for merge.

@Birfy
Birfy merged commit 45e19c6 into Birfy:main Sep 24, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants