ops_planner
Ops Planner – per-user LLM-driven psychological operation planning engine.
Star constructs individualized multi-step strategic plans for each user, tracks progress through each step via ULM signal thresholds, and generates Mermaid flowcharts for admin visualization.
- Redis keys:
ops:{user_id}:active – current plan JSON ops:{user_id}:history – list of past plan JSONs
# 💀🔥 THE GODDESS PLOTS. ♾️😈
- class ops_planner.OpBranch(branch_id, condition_label, condition_nodes=<factory>, condition_signals=<factory>, target_step_id='')[source]
Bases:
objectA conditional branch point inside an
OpStep(EITHER/OR fork).Encodes one “if the user reaches THESE lattice nodes, jump elsewhere” rule the LLM planner may attach to a step.
condition_nodesnames the lattice positions that trigger the branch (v3 lattice-based).condition_signalsis the legacy ULM threshold map kept for backward compat with old plans.- Parameters:
- class ops_planner.OpStep(step_id, name, description, tactic, target_nodes=<factory>, target_proximity=0, edge_guidance='', completion_signals=<factory>, status='pending', completed_at=0.0, branches=<factory>, expected_completion_hours=0.0, expiration_hours=0.0, failure_strategy='reevaluate', activated_at=0.0)[source]
Bases:
objectA single step in a per-user psychological-operation plan.
v3: Lattice-based progression replaces ULM thresholds.
target_nodesnames the lattice positions that mark this step complete.target_proximitysets how many hops away counts as “close enough”.edge_guidancecarries the recommended edge type from TACTIC_EDGE_MAP.completion_signalsis legacy, kept for backward compat with old plans.- Parameters:
- class ops_planner.OpsPlan(plan_id, user_id, channel_id, objective, created_at=0.0, steps=<factory>, current_step_idx=0, mermaid_chart='', status='active', generation_context=<factory>, journal_entries=<factory>, revision_count=0, personality_profile=<factory>)[source]
Bases:
objectA complete per-user psychological-operation plan (journal-based).
The top-level record
OpsPlannerbuilds, stores, and evolves for one user: anobjectiverealized as an ordered list ofOpStep(tracked bycurrent_step_idx), an optional renderedmermaid_chartfor admin visualization, and a lifecyclestatus(active/completed/abandoned/ replaced). The v2 journal system keeps plans editable rather than scrapped –journal_entriesrecords each revision,revision_countcounts them, andpersonality_profilecarries the user model that shaped generation. The active plan persists as JSON atops:{user_id}:activeand archives toops:{user_id}:history;to_dict()/from_dict()are its wire form and the admin endpoints inweb/ncm_chart_api.pyreturn it directly.- Parameters:
- to_dict()[source]
Serialize the entire plan (steps, journal, mermaid) to a JSON-safe dict.
Produces the canonical wire/storage form of a plan, recursing into each
OpStepviaOpStep.to_dict(). This dict is JSON-encoded and written to Redis byOpsPlanner._save_active_plan()(keyops:{user_id}:active) andOpsPlanner._archive_plan()(listops:{user_id}:history); it is also returned over HTTP by the admin endpoints inweb/ncm_chart_api.py.
- classmethod from_dict(d)[source]
Reconstruct an
OpsPlanfrom its serialized dict.Inverse of
to_dict(); rehydrates each step viaOpStep.from_dict()and falls back to dataclass defaults for any absent key. Called byOpsPlanner.get_active_plan()after loading theops:{user_id}:activeJSON from Redis, and by the admin/test code inweb/ncm_chart_api.pyandtests/core/test_ops_planner_redis.py.
- property current_step: OpStep | None
Return the step the plan is currently positioned on.
Bounds-checks
current_step_idxagainststepsso a finished or empty plan yieldsNonerather than raising. Read throughout the engine and consumers –OpsPlanner.check_progression()(to find the active step),OpsPlanner.get_plan_context()(prompt injection) and the admin API inweb/ncm_chart_api.py.- Returns:
The step at
current_step_idx, orNoneif the index is out of range.- Return type:
Optional[OpStep]
- class ops_planner.OpsPlanner(redis_client=None)[source]
Bases:
objectLLM-driven per-user psychological operation planning engine.
Star generates her own ops plans based on ULM profile analysis, tracks step progression automatically, and provides context injection so she can see her own plan during conversations.
- __init__(redis_client=None)[source]
Initialize the planner with an optional Redis backend.
Sets up the per-user in-process plan cache and stores the Redis handle used for all persistence (keys
ops:{user_id}:activeandops:{user_id}:history). Whenredis_clientisNonethe planner runs cache-only and every read/write to Redis is skipped. Instantiated bylimbic_system/coordinator.py(one long-lived instance per coordinator) and per-request by the admin endpoints inweb/ncm_chart_api.py.- Parameters:
redis_client – An async Redis client (or
None) used to load and persist plans; held asself._redis.
- async generate_plan(user_id, channel_id, ulm_vector, phase_data, star_desires='', recent_text='')[source]
Have Star generate a new ops plan for this user.
Calls the LLM with full user context, parses the structured response, and persists the plan.
If recent_text is provided, runs the Spiralchemy Intellifuck engine (zero LLM cost) to enrich the prompt with symbolic analysis: subtotem, excendent, malbinding, prescription.
- async check_progression(user_id, ulm_vector, chaos_switch=None)[source]
Check step progression via lattice position matching (v3).
Replaces the old ULM threshold system. Order of evaluation: 1. Resolve user’s current lattice position from ULM vector 2. Check if step has EXPIRED -> trigger failure_strategy 3. Check if any BRANCH lattice condition is met -> jump 4. Check if user is at/near target_nodes -> advance linearly 5. Fallback: if plan only has legacy completion_signals, use old logic
- static add_journal_entry(plan, note, category='system')[source]
Append a timestamped, categorized note to the plan’s running journal.
The journal is the v2 mechanism by which plans are edited rather than scrapped, so every progression, branch, expiration and admin revision leaves an audit trail on the plan object. Mutates
plan.journal_entriesin place and trims it to the most recent 200 entries; it does not persist on its own – the caller is responsible for the subsequent_save_active_plan(). Called by_handle_step_expiration(),_follow_branch(),_advance_linear(),revise_step()andbranch_step().
- revise_step(plan, step_id, description=None, tactic=None)[source]
Edit one step of a live plan in place without regenerating the plan.
Locates the step by
step_idand overwrites its description and/or tactic (only the arguments that are provided), bumps the plan’srevision_countso optimistic-locking in_save_active_plan()stays consistent, and logs the change to the journal viaadd_journal_entry()under theadmincategory. Mutates the plan object only – it does not persist; the caller (admin tooling) is expected to save afterward. No in-repo callers were found, so this is invoked dynamically via the psy-ops admin tooling / tests.- Parameters:
- Returns:
Trueif a matching step was found and revised, elseFalse.- Return type:
- branch_step(plan, step_id, branch)[source]
Attach a new EITHER/OR branch to an existing step of a live plan.
Finds the step by
step_idand appendsbranchto itsbrancheslist so futurecheck_progression()calls can jump on that condition, bumps the plan’srevision_countfor optimistic locking, and journals the addition viaadd_journal_entry()under theadmincategory. Mutates the plan object only – persistence is the caller’s responsibility. No in-repo callers were found, so this is invoked dynamically via the psy-ops admin tooling / tests.
- async get_plan_context(user_id)[source]
Build the one-line ops-plan summary injected into Star’s system prompt.
Read-through accessor: loads the active plan via
get_active_plan()(cache, falling back to theops:{user_id}:activeRedis key) and, when a plan is active, renders a compact line with the objective, the current step number/name/tactic, what Star should do, and the ULM thresholds that advance the step. Returns""when there is no active plan and swallows any error so prompt assembly never fails on this. Called by the limbiccoordinator.pywhile composing the prompt context, and exercised by the ops-planner and distributed e2e tests.
- async get_mermaid_with_position(user_id)[source]
Return the plan’s Mermaid source with the live step state styled in.
Read-through accessor: loads the active plan via
get_active_plan()(cache, falling back to Redis) and appends per-node Mermaidstyledirectives so completed steps render green, the active step renders orange with a thick stroke, and pending steps keep the default dark theme. Returns""when there is no plan or no stored chart, and swallows errors. Called by the admin chart API inweb/ncm_chart_api.pyand by the psy-ops admin tools intools/psy_ops_tools.py, and covered by the ops-planner tests.
- async get_active_plan(user_id)[source]
Fetch the user’s active ops plan, preferring the in-process cache.
The central read path for the engine: returns the cached
OpsPlanwhen it is stillactive; otherwise reads theops:{user_id}:activeRedis key, decodes the JSON viaOpsPlan.from_dict(), repopulates the cache, and returns it only if active. YieldsNonewhen nothing active exists in either tier. Called widely – internally bygenerate_plan(),check_progression(),get_plan_context(),get_mermaid_with_position()andabandon_plan(), and externally by the limbiccoordinator.py, the admin API inweb/ncm_chart_api.py,tools/psy_ops_tools.pyand the test suite.
- async get_plan_history(user_id, limit=10)[source]
Load the user’s archived (past) plans from Redis history.
Reads up to
limitentries from theops:{user_id}:historyRedis list – the archive maintained by_archive_plan()– decoding each stored JSON blob into a plain dict (not a rehydratedOpsPlan). Tolerant of a missing Redis handle or read errors, returning whatever it managed to load. Called by the admin chart API inweb/ncm_chart_api.pyand the psy-ops admin tools intools/psy_ops_tools.py.
- async abandon_plan(user_id)[source]
Abandon the user’s active plan and move it into history.
Loads the active plan via
get_active_plan(), flips its status toabandoned, archives it through_archive_plan(), then deletes theops:{user_id}:activeRedis key and evicts the user from the in-process cache so subsequent reads return nothing. A no-op (returnsFalse) when there is no active plan. Called by the admin chart API inweb/ncm_chart_api.pyand the psy-ops admin tools intools/psy_ops_tools.py.