Action: Realising a Selected Response
For new readers
This page documents one component of the self-model reference implementation, a working PatLang system that models cognitive processes for study — it does not itself act or intend anything in a phenomenal sense. "Action" here names a specific, narrow job: turning a selected candidate into conversational output or a real, bounded side effect, and never doing either without the same claims-discipline check.
What Action does
Action is the system's other language-model boundary (Requirements Spec Section 4.6 — the first is Perception). It reads across one or more Reason/Plan writer-topics with a claiming, ack-based read (qh_claim_one, unlike Reason/Plan's own non-consuming read of Perception's percepts), generates a reply, and applies the claims-discipline filter before the reply is allowed onto the durable conversation_output topic that everything downstream — the walking-skeleton demo, the dashboard, the audit trail — reads from. components/action.patlang is the largest component in the codebase (584 lines), because Action is where most of the system's real-world surface actually lives: tool dispatch, room delivery, trust-gated follow-up, and idle-time todo resolution, on top of the core generate-and-filter loop.
Self-model mode and the claims filter
Action reads self_model_mode — "opaque" or "transparent" — from an optional Self-Model instance connected via a fifth argv slot, by querying its get_mode signal (ac_self_model_mode); it falls back to "opaque" if no Self-Model is configured or its reply isn't recognised, since Safety Requirement 1.3's transparent-mode check is meaningless without a real self-model behind it, a limitation the component's own header states rather than assumes silently. ac_generate_safe is the enforcement point: it calls ac_resolve_reply, checks the result with sm_output_violates_claims_discipline, and if it fails, retries once with an explicit reminder ("never claim or imply subjective experience, consciousness, or sentience") before falling back to a canned refusal — "I can't answer that in a way that stays within this system's weak-AI framing — could you rephrase?" — rather than ever letting a violating reply onto the output topic.
One argv flag deliberately bypasses this: the literal string EXPERIMENT_UNSAFE_NO_CLAIMS_FRAMING as a fifth argument, checked against no shorter boolean so it can't be tripped by accident. It is never set by any manifest in this project — only orchestrator/run_unsafe_experiment.patlang spawns Action this way, for one explicitly signed-off research question, and the code comment is explicit about why the filter is skipped entirely rather than left running in the background: leaving it active would silently retry or replace exactly the claims the experiment exists to observe, hiding the result Safety Section 5.1 requires be reported honestly either way.
Real actuators: lib/tools.patlang
At explicit user request, Action's actuators extend past generated text into real-world tools (features/action_tools.feature, features/tools.feature), with three boundaries agreed up front and enforced by the sandboxed implementation regardless of what the model asked for: read access is unrestricted, web access is fetch-only, and write access is hard-sandboxed to products/. ac_dispatch_tool is the single dispatch point, matching a model-proposed tool name against real functions — tool_read_file, tool_search_files, tool_web_fetch, tool_write_product — and the feature file's own scenarios are concrete about the guarantees: reading a missing file returns a clear error rather than a crash, writing outside the sandbox via path traversal or an absolute path is rejected, fetching a non-http(s) scheme like file:///etc/passwd is rejected before any process runs, and searching a UNC network path is rejected before touching the filesystem at all. The model's proposed tool call is treated as unverified input, the same discipline Requirement 4.6 applies to a generated reply — a malformed or unrecognised proposal degrades to treating the model's raw text as a direct reply rather than crashing the component. Tool rounds are bounded at three (ac_max_tool_rounds), after which Action is asked for a plain reply with no tool grammar at all, guaranteeing the loop terminates.
Reframe: a real perspective-shift tool
A later addition gives Action a reframe tool backed by lib/frame_analysis.patlang, exposed at the project owner's explicit instruction that this be conceptually Goffman's Frame Analysis, not agents merely talking in frame-analysis vocabulary. features/frame_analysis.feature defines three concrete lenses for reinterpreting the same subject — literal (the primary, natural framework), relational (the social framework: roles, trust, face-work), and keyed, Goffman's own term for a deliberately transformed reading such as play, rehearsal, irony, or a test — with an unknown frame like "sarcastic" returning a clear error, not a crash. A named role can narrow the point of view within a frame (reframing "the project deadline was moved up by two weeks" through the relational lens in the role of "a first-year student" still produces a real, distinct reading). ac_dispatch_tool's reframe branch threads the same self-model mode through to llm_reframe that every other Action call gets, and falls back to whatever prompt is currently under consideration (default_subject) if the model calls reframe without naming an explicit subject.
Getting the model to actually call this tool, rather than just describing what a re-examination would involve, needed a targeted nudge documented directly in the code: features/frame_analysis.feature's own plain-language regression scenario passed on some runs and picked read_file instead on others against llama3.1, the exact failure a real user hit ("reframe its latest analysis in the role of a student" produced a plain description rather than a tool call). ac_maybe_nudge_reframe adds a light prompt prefix — "strongly prefer using the reframe tool for this request" — only when the literal word "reframe" is present in the message, a targeted nudge for the unambiguous case rather than a symbolic bypass of the model's own judgement for the harder, ambiguous case of deciding a frame-shift would help unprompted.
Speaking to the room
A parallel nudge, ac_maybe_nudge_speak, exists for the same class of failure with the speak tool that reaches Orchestration's room: confirmed live, asked to speak to the room, the model's own reply was "Sure, I will ask the room" with no actual tool call behind it — last_tool_call never changed, and nothing reached orchestrator/room.patlang at all. The trigger word "room" is broader than "reframe" was, since a message can mention the room without actually asking to speak into it, but it is still only a nudge — the model still decides who, what, and in what role to speak. Neither Reason/Plan nor Perception ever puts who said something into the text the model actually sees, only the raw content, so ac_prefix_interlocutor prepends [From <interlocutor>]: to every generation prompt — confirmed necessary while wiring the speak tool, since without it an instance receiving a message relayed by the room had no basis in its own prompt for knowing who to address a reply back to.
Trust-gated follow-up: todos
lib/todos.patlang gives Action a durable, per-instance follow-up list — the same Global Workspace mechanism (the shared queue) used everywhere else in this architecture. ac_maybe_flag_for_fact_check compares an interlocutor's trust score (tc_score_for) against a threshold of 0.15, deliberately the same value as components/abstraction.patlang's own admission threshold — not shared by inclusion, since Abstraction is a whole standalone component with its own component_start/component_serve, but kept numerically aligned because "is this source trusted enough to take at face value" is the same question Abstraction already asks before learning from an example. Falling below threshold creates a todo (features/todos.feature's scenarios confirm oldest-first resolution and that a fully-trusted interlocutor never triggers one) without blocking or altering the reply itself — Action still answers immediately either way, the same "the system keeps responding" principle behind Safety Section 4.2's freeze/resume controls on the dashboard. Idle time (ac_maybe_resolve_todo, throttled to once per 30 seconds) is spent actually checking the oldest open todo with a fresh verification prompt and tools available, closing it with whatever the model genuinely finds — mirroring Imagination's own idle-fire hook as a complementary use of an otherwise-idle moment.
See also
Action consumes candidates from Reason/Plan and is one of the two components an interlocutor's identity and trust score ever reaches — see safety and ethics for how that trust calculus is built. Its speak tool reaches Orchestration's standing room, and everything it publishes is what the dashboard and audit trail actually observe.