Abstraction: Inducing Categories, Trust-Gated
Abstraction watches the same firehose of Short Term content that every other component draws on, and does one specific job with it: it induces general categories from repeated entity/intent pairs, then answers a standing question — categorize(entity) — with whichever induced category currently accepts that entity, or "unknown" if none does. What makes it more than a lookup table is where the categories come from and who is allowed to add one.
One mechanism, two edges
Abstraction's induction step is not bespoke logic. It calls the same rule_add/solve synthesis engine (F:/patlang/self_hosting/lib/synthesis.patlang) that Procedural Memory uses for a different purpose elsewhere in this architecture — one mechanism serving two edges of the system, per the architecture document. In components/abstraction.patlang, a category is registered as a fact-and-rule pair against a first-mentioned entity and an intent, and categorize walks the list of known categories asking solve(category, [synth_ground(entity)]) until one succeeds.
The comment at the top of the source file is explicit about a limitation this induces: the engine is existence-based, not frequency-weighted. A fact either exists in the store or it doesn't, so calling the synthesis step on an identical example twice changes nothing. That rules out the obvious implementation of "weight admission by trust" — you cannot make a fact "more true" by repeating it. Abstraction handles this honestly rather than faking a repeat-count scheme against a mechanism that wouldn't respond to one: trust is applied as a binary admission threshold. An example is admitted once, in full, or not admitted at all.
The gate itself
Every incoming percept carries an interlocutor. Abstraction asks lib/trust_calculus.patlang's tc_score_for(interlocutor) for that interlocutor's current trust score and compares it against abstraction_admission_threshold(), fixed at 0.15. Trust itself is not a single number kept in the interlocutor's head; it's a three-component vector [T_positive, T_negative, T_uncertainty], persisted to disk so a restarted Abstraction instance doesn't forget who it has learned to distrust. The update rule tc_update nudges the positive component up by alpha = 0.1 on a positive event and the negative component up by beta = 0.3 on a negative one — trust builds slowly and is damaged quickly, exactly the asymmetry the trust-calculus page this code implements argues for. The score handed to Abstraction is clamp01(pos - neg), floored at zero rather than going negative.
Below the threshold, the example is not silently dropped — it is consumed off the queue and acknowledged, logged via cc_log_decision as not admitted, but it never reaches rule_add at all. A zero-history interlocutor's very first example is therefore always rejected outright: an unseen interlocutor starts at [0, 0, 1], giving a trust score of exactly 0, below the 0.15 bar. One exception is deliberate: tc_bootstrap_interlocutor() ("system_bootstrap") starts at [1, 0, 0] — fully trusted from the outset, standing in for pre-vetted, developer-supplied seed examples rather than an open interlocutor that has to earn its way in.
features/trust_calculus.feature exercises this directly: an unseen interlocutor scores 0; three positive events bring a score close to 0.3; a single subsequent negative event drops the score by more than one positive event's worth, confirming the build-slowly/damage-quickly asymmetry rather than just asserting it. features/abstraction.feature then closes the loop end to end — a percept about "Paris" with intent "travel" from a trust-cleared "trusted_user" gets admitted, and categorizing "Paris" subsequently returns "travel"; the same percept about "Berlin" from a zero-history "new_user" is rejected, and categorizing "Berlin" returns "unknown".
Freeze, checkpoint, rollback — and an honest limit on rollback
Abstraction can be frozen (freeze_compilation/resume_compilation): while frozen, new examples are logged as not-admitted regardless of trust, but categorize keeps answering from whatever was already compiled — a human reviewer can halt further learning without taking the component's existing knowledge offline. It also supports checkpoint and rollback against its persisted examples file. The source comments are candid about a real constraint here: PatLang's rule_add has no corresponding retract. A fact already registered in a live process's fact store cannot be un-registered from that same process, so a rollback rewrites the file on disk correctly but only takes full effect once the component restarts and indstore_replay rebuilds the fact store fresh from the rolled-back file — the same rebuild step an ordinary restart already performs. The rollback handler therefore replies and then calls component_request_stop() itself, so the fix is a brief, self-contained restart rather than a claim of instantaneous retraction it can't actually deliver.
Feeding back into Perception
Every admission also publishes to the schema-feedback channel (lib/schema_feedback.patlang), a plain-text line like "schema: entities like 'Paris' tend to have intent 'travel'". Perception instances only ever peek at this topic — never consume it — so several Perception instances can each read the same accumulated schema expectations without racing each other to claim entries away. This is the concrete shape of "Abstraction feeds back into Perception as schema-driven expectation" from the requirements spec, and it is the same channel that Self-Model's transparent mode reuses for its own predictions, discussed on that page.
Like every long-running component in this codebase, Abstraction composes lib/component_base.patlang rather than inheriting a base class — PatLang's signal handlers don't support per-instance dispatch, so shared lifecycle (status queries, a quit handler, periodic re-announcement for dashboard discovery) is reused via included modules. That machinery is exercised generically in features/component_lifecycle.feature, not reproduced per component.
See also
Abstraction's output feeds Representation (via the schema-driven expectation loop back through Perception) and gates what Imagination is allowed to recombine — only episodes whose entity currently categorizes as known are eligible. For the fuller architectural picture of how these components fit together, see the architecture page.