feat(learn): per-repository role inference, framework-free (decision-engine task 3)

Wrapper inference (learn/roles.py, no model): a function whose parameter reaches a
call already in the role set becomes a derived sink (or a sanitizer when it returns
the cleaned value) of the same operation, at depth + 1; a function returning a
source's value becomes a derived source. The role set starts from language-level
entries only (priors off by default, optional), grows to depth 3, and reads
vendored dependency source when present, so a vendored database class reaching
mysqli makes `->query` a sink without a framework table. A name the repository
declares more than once becomes a role only when every definition derives it.

Model roles (plane/tasks/roles.py): the model_sinks.py classification moved into
the package and extended to sink/source/sanitizer/guard/dispatch, per file chunk,
metered and resumable, client injected; a failed chunk is unclassified, never
"no roles". vocabulary_overlay merges inferred and model roles over the
language-level facts in the shape taint_specs takes, origin in the entry id.
Features: the roles (static) and model_sinks (plane) instruments are filled.

On the recorded validation-46 facts (15 cases, 14,368 product files, 87 MB read):
1,999 wrapper roles (1,753 sinks, 157 sources, 89 sanitizers; depth 1/2/3 =
973/651/375), 14 of 15 cases with at least one; the WordPress gopay plugin has
none with priors off, which is what model roles are for.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
Marius Ciepluch
2026-09-30 19:16:54 +02:00
co-authored by Claude Opus 5.5
parent 1fcc31c857
commit 884092e62c
8 changed files with 1161 additions and 28 deletions
@@ -1,7 +1,7 @@
{
"modules": 140,
"modules": 142,
"counts": {
"load_bearing": 133,
"load_bearing": 135,
"standalone_capability": 7
},
"orphaned": [],
@@ -352,8 +352,10 @@
"reason": "reachable from gate, map_gate, pair_gate or the CLI",
"importers": [
"learn.features",
"learn.roles",
"plane.tasks.features",
"plane.tasks.remember"
"plane.tasks.remember",
"plane.tasks.roles"
]
},
{
@@ -364,13 +366,25 @@
"plane.tasks.features"
]
},
{
"module": "learn.roles",
"classification": "load_bearing",
"reason": "imported by learn.features, plane.tasks.features, plane.tasks.roles",
"importers": [
"learn.features",
"plane.tasks.features",
"plane.tasks.roles"
]
},
{
"module": "learn.schema",
"classification": "load_bearing",
"reason": "reachable from gate, map_gate, pair_gate or the CLI",
"importers": [
"learn.features",
"plane.tasks.remember"
"learn.roles",
"plane.tasks.remember",
"plane.tasks.roles"
]
},
{
@@ -702,6 +716,7 @@
"plane.runner",
"plane.tasks.loop",
"plane.tasks.remember",
"plane.tasks.roles",
"plane.tasks.verify",
"plane.workspaces"
]
@@ -712,6 +727,7 @@
"reason": "ai-service-plane Req 2.3/2.4: metered chat client with usd and calls ceilings; task integration in tasks 3, 6",
"importers": [
"plane.generate",
"plane.tasks.roles",
"plane.tasks.verify"
]
},
@@ -801,13 +817,15 @@
"importers": [
"cli",
"learn.features",
"learn.roles",
"plane.engine_alerts",
"plane.generate",
"plane.memory",
"plane.tasks.agree",
"plane.tasks.alerts",
"plane.tasks.features",
"plane.tasks.remember"
"plane.tasks.remember",
"plane.tasks.roles"
]
},
{
@@ -854,19 +872,28 @@
"reason": "ai-service-plane Req 5.1/5.2: source-only functions and callers per file; runner integration pending",
"importers": [
"learn.features",
"learn.roles",
"plane.generate",
"plane.tasks.alerts",
"plane.tasks.features",
"plane.tasks.remember"
"plane.tasks.remember",
"plane.tasks.roles"
]
},
{
"module": "plane.tasks.roles",
"classification": "load_bearing",
"reason": "learned-decision-engine Req 8.1: model role classification per file chunk (model_sinks.py moved in); its Run template comes with the harvest (task 5)",
"importers": []
},
{
"module": "plane.tasks.verify",
"classification": "load_bearing",
"reason": "ai-service-plane Req 5.3/2.1-2.3/7.2: the batched hunt with known callers, metered and resumable per file hunt",
"importers": [
"plane.generate",
"plane.tasks.agree"
"plane.tasks.agree",
"plane.tasks.roles"
]
},
{
@@ -917,6 +944,7 @@
"improve.evolve",
"index",
"learn.features",
"learn.roles",
"map_gate",
"mapping",
"model.candidates",
@@ -1195,6 +1223,7 @@
"improve.memory",
"improve.validator",
"learn.features",
"learn.roles",
"pairs",
"plane.tasks.alerts",
"plane.tasks.features",
@@ -1209,6 +1238,7 @@
"classification": "load_bearing",
"reason": "reachable from gate, map_gate, pair_gate or the CLI",
"importers": [
"learn.roles",
"ruleset.store",
"semantic.facts"
]
@@ -1289,6 +1319,7 @@
"cpg.capability",
"hunter_tools",
"learn.features",
"learn.roles",
"mapping",
"model.candidates",
"model.layout",
@@ -1344,6 +1375,7 @@
"cli",
"hunter_tools",
"learn.features",
"learn.roles",
"mapping",
"model.candidates",
"model.layout",
@@ -1550,6 +1582,7 @@
"importers": [
"model.endpoint",
"plane.budget",
"plane.tasks.roles",
"plane.tasks.verify"
]
},