Add camera inventory audit and expand event catalog actions.

- Introduce "camera_a_inventory_ok" check in AgentHarness for inventory audits
- Add new actions to event catalog for civilization founding and reproduction
- Update decision catalog to include new action phrases and signal predicates
- Increment version to 0.28.27 in mod.json
This commit is contained in:
DazedAnon 2026-07-17 18:37:10 -05:00
parent ef561b87d8
commit f10424c5df
10 changed files with 657 additions and 95 deletions

View file

@ -7326,6 +7326,13 @@ public static class AgentHarness
+ $"kinds={kinds.Count}";
break;
}
case "camera_a_inventory_ok":
{
CameraAInventoryResult inv = CameraAInventoryHarness.RunAudit(HarnessDir());
pass = inv.Passed;
detail = inv.Detail;
break;
}
case "library_ambient_policy_ok":
{
// Signal-only libraries: ambient dials must be B; phenotypes all B;

View file

@ -0,0 +1,278 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace IdleSpectator;
public sealed class CameraAInventoryResult
{
public bool Passed;
public string Detail = "";
public int Total;
public int CameraA;
public int CameraB;
}
/// <summary>
/// Live dump of Layer A vs B fate for decisions + library dial domains.
/// Source of truth for Signal / decision promote work (never promote from memory).
/// </summary>
public static class CameraAInventoryHarness
{
public static CameraAInventoryResult RunAudit(string outputDirectory)
{
var lines = new List<string>
{
"domain\tid\tdisplay\tlayer\tstrength\treason"
};
int total = 0;
int cameraA = 0;
int cameraB = 0;
var summary = new StringBuilder();
AppendDomain(
lines,
ref total,
ref cameraA,
ref cameraB,
summary,
"decisions",
"decision",
ActivityAssetCatalog.EnumerateLiveDecisionIds(),
id => EventCatalog.Decision.GetOrFallback(id),
id => EventCatalog.Decision.IsCameraWorthy(id)
? (EventCatalog.Decision.TryGetAuthoredActionPhrase(id, out _)
? "signal_predicate+phrase"
: "signal_predicate")
: ReasonDecisionB(id));
AppendDomain(
lines, ref total, ref cameraA, ref cameraB, summary,
"spells", "spell",
ActivityAssetCatalog.EnumerateLiveSpellIds(),
EventCatalog.Spell.GetOrFallback,
id => ReasonLibrary(EventCatalog.Spell.GetOrFallback(id)));
AppendDomain(
lines, ref total, ref cameraA, ref cameraB, summary,
"powers", "power",
ActivityAssetCatalog.EnumerateLivePowerIds(),
EventCatalog.Power.GetOrFallback,
id => ReasonLibrary(EventCatalog.Power.GetOrFallback(id)));
AppendDomain(
lines, ref total, ref cameraA, ref cameraB, summary,
"items", "item",
ActivityAssetCatalog.EnumerateLiveItemIds(),
EventCatalog.Item.GetOrFallback,
id => ReasonLibrary(EventCatalog.Item.GetOrFallback(id)));
AppendDomain(
lines, ref total, ref cameraA, ref cameraB, summary,
"genes", "gene",
ActivityAssetCatalog.EnumerateLiveGeneIds(),
EventCatalog.Gene.GetOrFallback,
id => ReasonLibrary(EventCatalog.Gene.GetOrFallback(id)));
AppendDomain(
lines, ref total, ref cameraA, ref cameraB, summary,
"phenotypes", "phenotype",
ActivityAssetCatalog.EnumerateLivePhenotypeIds(),
EventCatalog.Phenotype.GetOrFallback,
id => ReasonLibrary(EventCatalog.Phenotype.GetOrFallback(id)));
AppendDomain(
lines, ref total, ref cameraA, ref cameraB, summary,
"world_laws", "worldlaw",
ActivityAssetCatalog.EnumerateLiveWorldLawIds(),
EventCatalog.WorldLaw.GetOrFallback,
id => ReasonLibrary(EventCatalog.WorldLaw.GetOrFallback(id)));
AppendDomain(
lines, ref total, ref cameraA, ref cameraB, summary,
"subspecies_traits", "subspecies_trait",
ActivityAssetCatalog.EnumerateLiveSubspeciesTraitIds(),
EventCatalog.SubspeciesTrait.GetOrFallback,
id => ReasonLibrary(EventCatalog.SubspeciesTrait.GetOrFallback(id)));
AppendDomain(
lines, ref total, ref cameraA, ref cameraB, summary,
"biomes", "biome",
ActivityAssetCatalog.EnumerateLiveBiomeIds(),
EventCatalog.Biome.GetOrFallback,
id => ReasonLibrary(EventCatalog.Biome.GetOrFallback(id)));
AppendDomain(
lines, ref total, ref cameraA, ref cameraB, summary,
"culture_traits", "culture_trait",
ActivityAssetCatalog.EnumerateLiveCultureTraitIds(),
EventCatalog.CultureTrait.GetOrFallback,
id => ReasonLibrary(EventCatalog.CultureTrait.GetOrFallback(id)));
AppendDomain(
lines, ref total, ref cameraA, ref cameraB, summary,
"religion_traits", "religion_trait",
ActivityAssetCatalog.EnumerateLiveReligionTraitIds(),
EventCatalog.ReligionTrait.GetOrFallback,
id => ReasonLibrary(EventCatalog.ReligionTrait.GetOrFallback(id)));
AppendDomain(
lines, ref total, ref cameraA, ref cameraB, summary,
"clan_traits", "clan_trait",
ActivityAssetCatalog.EnumerateLiveClanTraitIds(),
EventCatalog.ClanTrait.GetOrFallback,
id => ReasonLibrary(EventCatalog.ClanTrait.GetOrFallback(id)));
AppendDomain(
lines, ref total, ref cameraA, ref cameraB, summary,
"language_traits", "language_trait",
ActivityAssetCatalog.EnumerateLiveLanguageTraitIds(),
EventCatalog.LanguageTrait.GetOrFallback,
id => ReasonLibrary(EventCatalog.LanguageTrait.GetOrFallback(id)));
try
{
if (!string.IsNullOrEmpty(outputDirectory))
{
Directory.CreateDirectory(outputDirectory);
File.WriteAllLines(Path.Combine(outputDirectory, "camera-a-inventory.tsv"), lines);
File.WriteAllText(
Path.Combine(outputDirectory, "camera-a-inventory-summary.txt"),
summary.ToString());
}
}
catch
{
// diagnostic dump only
}
bool passed = total > 0 && cameraA > 0;
return new CameraAInventoryResult
{
Passed = passed,
Total = total,
CameraA = cameraA,
CameraB = cameraB,
Detail = $"total={total} A={cameraA} B={cameraB} domains=13"
};
}
private static void AppendDomain(
List<string> lines,
ref int total,
ref int cameraA,
ref int cameraB,
StringBuilder summary,
string domain,
string displayKind,
List<string> ids,
Func<string, DiscreteEventEntry> getEntry,
Func<string, string> reasonFor)
{
if (ids == null)
{
ids = new List<string>();
}
int a = 0;
int b = 0;
for (int i = 0; i < ids.Count; i++)
{
string id = ids[i];
if (string.IsNullOrEmpty(id))
{
continue;
}
DiscreteEventEntry entry = getEntry(id);
bool isA = entry != null
&& EventCatalog.IsCameraWorthy(entry);
string layer = isA ? "A" : "B";
if (isA)
{
a++;
cameraA++;
}
else
{
b++;
cameraB++;
}
total++;
string display = domain == "decisions"
? EventReason.HumanizeId(id)
: LibraryAssetNames.DisplayName(displayKind, id);
display = SanitizeTsv(display);
float strength = entry != null ? entry.EventStrength : 0f;
string reason = SanitizeTsv(reasonFor(id) ?? "");
lines.Add(
domain + "\t" + id + "\t" + display + "\t" + layer + "\t"
+ strength.ToString("0.#") + "\t" + reason);
}
summary.AppendLine(domain + "\tlive=" + ids.Count + "\tA=" + a + "\tB=" + b);
}
private static string ReasonLibrary(DiscreteEventEntry entry)
{
if (entry == null)
{
return "missing";
}
if (entry.IsFallback)
{
return "fallback";
}
return entry.CreatesInterest ? "signal" : "ambient";
}
private static string ReasonDecisionB(string id)
{
if (string.IsNullOrEmpty(id))
{
return "empty";
}
string s = id.ToLowerInvariant();
if (s == "try_new_plot" || s.StartsWith("try_new_plot_", StringComparison.Ordinal))
{
return "deny_plot_intent";
}
if (s.Contains("idle")
|| s.Contains("walking")
|| s.Contains("check")
|| s.Contains("wait")
|| s.Contains("sleep")
|| s.Contains("random")
|| s.Contains("play")
|| s.Contains("jump")
|| s.Contains("flip")
|| s.Contains("diet_")
|| s.Contains("move")
|| s.Contains("swim")
|| s.Contains("follow_parent")
|| s.Contains("follow_desire")
|| s.Contains("reflection")
|| s.Contains("poop"))
{
return "deny_fluff";
}
return "no_signal_token";
}
private static string SanitizeTsv(string value)
{
if (string.IsNullOrEmpty(value))
{
return "";
}
return value.Replace('\t', ' ').Replace('\n', ' ').Replace('\r', ' ');
}
}

View file

@ -51,6 +51,37 @@ public static partial class EventCatalog
["warrior_army_follow_leader"] = "follow their leader",
["warrior_train_with_dummy"] = "train with a dummy",
["warrior_try_join_army_group"] = "try to join an army",
["warrior_army_leader_move_to_attack_target"] = "lead the army to attack",
// Founding / territory
["try_to_start_new_civilization"] = "start a new civilization",
["build_civ_city_here"] = "found a city here",
["claim_land"] = "claim this land",
["king_check_new_city_foundation"] = "found a new city",
["bee_create_hive"] = "create a hive",
// Life / pack
["sexual_reproduction_try"] = "try to reproduce",
["asexual_reproduction_budding"] = "reproduce by budding",
["asexual_reproduction_divine"] = "reproduce by divine means",
["asexual_reproduction_fission"] = "reproduce by fission",
["asexual_reproduction_parthenogenesis"] = "reproduce by parthenogenesis",
["asexual_reproduction_spores"] = "reproduce by spores",
["asexual_reproduction_vegetative"] = "reproduce vegetatively",
["family_group_follow"] = "follow their family group",
["family_group_join_or_new_herd"] = "join or form a herd",
["family_group_leave"] = "leave their family group",
// Spectacle / dark / civic
["attack_golden_brain"] = "attack the golden brain",
["make_skeleton"] = "raise a skeleton",
["status_soul_harvested"] = "have their soul harvested",
["possessed_following"] = "follow under possession",
["try_to_take_city_item"] = "try to take a city item",
["put_out_fire"] = "put out a fire",
["burn_tumors"] = "burn tumors",
["try_to_launch_fireworks"] = "launch fireworks",
["try_affect_dreams"] = "affect dreams",
};
/// <summary>Drop cached rows so the next Ensure re-applies JSON overlays.</summary>
@ -68,26 +99,6 @@ public static partial class EventCatalog
}
string s = id.ToLowerInvariant();
// Pure AI ticks / daily fluff - not story beats.
if (s.Contains("idle")
|| s.Contains("walking")
|| s.Contains("check")
|| s.Contains("wait")
|| s.Contains("sleep")
|| s.Contains("random")
|| s.Contains("play")
|| s.Contains("jump")
|| s.Contains("flip")
|| s.Contains("diet_")
|| s.Contains("move")
|| s.Contains("swim")
|| s.Contains("follow_parent")
|| s.Contains("follow_desire")
|| s.Contains("reflection")
|| s.Contains("poop"))
{
return false;
}
// Intent-only: setDecisionCooldown fires before a Plot exists (and often never does).
// Real plot tips come from PlotInterestFeed / live World.plots.
@ -96,7 +107,15 @@ public static partial class EventCatalog
return false;
}
// Daily AI / check ticks. Foundation checks stay eligible for Signal tokens.
if (IsFluffDecision(s))
{
return false;
}
return HasToken(s, "war")
|| HasToken(s, "king")
|| HasToken(s, "leader")
|| s.Contains("rebellion")
|| s.Contains("revolt")
|| s.Contains("alliance")
@ -114,14 +133,79 @@ public static partial class EventCatalog
|| s.Contains("banish")
|| s.Contains("steal")
|| s.Contains("kill_unruly")
|| HasToken(s, "king")
|| HasToken(s, "leader")
|| s.Contains("clan")
|| s.Contains("religion")
|| s.Contains("culture")
|| s.Contains("baby_make")
|| s.Contains("have_child")
|| s.Contains("birth");
|| s.Contains("birth")
|| s.Contains("reproduction")
|| s.Contains("civilization")
|| s.Contains("civ_city")
|| s.Contains("claim_land")
|| s.Contains("golden_brain")
|| s.Contains("make_skeleton")
|| s.Contains("soul_harvest")
|| s.Contains("possessed")
|| s.Contains("create_hive")
|| s.Contains("fireworks")
|| s.Contains("affect_dreams")
|| s.Contains("family_group")
|| s.Contains("take_city_item")
|| s.Contains("put_out_fire")
|| s.Contains("burn_tumors");
}
/// <summary>
/// Pure AI ticks / daily fluff. Not story beats.
/// <c>city_foundation</c> checks are excluded so kings can Signal founding intent.
/// </summary>
private static bool IsFluffDecision(string s)
{
if (string.IsNullOrEmpty(s))
{
return true;
}
if (s.Contains("city_foundation"))
{
return false;
}
if (s.StartsWith("check_", StringComparison.Ordinal)
|| s.Contains("_check_")
|| s.EndsWith("_check", StringComparison.Ordinal))
{
return true;
}
return s.Contains("idle")
|| s.Contains("walking")
|| s.Contains("sleep")
|| s.Contains("play")
|| s.Contains("jump")
|| s.Contains("flip")
|| s.Contains("diet_")
|| s.Contains("reflection")
|| s.Contains("poop")
|| s.Contains("follow_parent")
|| s.Contains("follow_desire")
|| s.Contains("move_random")
|| s.Contains("random_move")
|| s.Contains("random_fun")
|| s.Contains("random_swim")
|| s.Contains("random_teleport")
|| s.Contains("random_emotion")
|| s.Contains("warrior_random")
|| s.Contains("captain_waiting")
|| s.Contains("captain_idle")
|| s.Contains("_waiting")
|| s == "wait5"
|| s.Contains("swim_to")
|| s.Contains("move_to_water")
|| s.Contains("bored_sleep")
|| s.Contains("monophasic")
|| s.Contains("polyphasic");
}
/// <summary>Underscore-token match so <c>war</c> does not hit <c>warrior</c>.</summary>

View file

@ -143,6 +143,17 @@ public static partial class EventCatalog
}
string s = id.ToLowerInvariant();
// God-tool editors / drip brushes stay B.
if (s.Contains("_edit")
|| s.Contains("traits_")
|| s.Contains("equipment_rain")
|| s.EndsWith("_brush", StringComparison.Ordinal)
|| s.Contains("draw_")
|| s.Contains("paint"))
{
return false;
}
return s.Contains("meteor")
|| s.Contains("earthquake")
|| s.Contains("tornado")
@ -150,12 +161,28 @@ public static partial class EventCatalog
|| s.Contains("nuke")
|| s.Contains("bomb")
|| s.Contains("rain_blood")
|| s.Contains("blood_rain")
|| s.Contains("hell")
|| s.Contains("lightning")
|| s.Contains("storm")
|| s.Contains("acid")
|| s.Contains("fire")
|| s.Contains("disaster");
|| s.Contains("disaster")
|| s.Contains("dragon")
|| s.Contains("demon")
|| s.Contains("plague")
|| s.Contains("zombie")
|| s.Contains("crabzilla")
|| s == "ufo"
|| s.Contains("god_finger")
|| s.Contains("cold_one")
|| s.Contains("heatray")
|| s.Contains("curse")
|| s.Contains("lava")
|| s.Contains("ash")
|| s.Contains("golden_brain")
|| s.Contains("corrupted_brain")
|| s.Contains("infection");
}
private static void Ensure()
@ -218,7 +245,8 @@ public static partial class EventCatalog
}
string s = id.ToLowerInvariant();
// Top-tier materials from live items library (mythril/adamantine) plus named relics.
// High / mid-spectacle materials from live items library plus named relics.
// Leather/wood/copper/iron stay ambient B.
return s.Contains("legendary")
|| s.Contains("mythic")
|| s.Contains("mythril")
@ -230,7 +258,12 @@ public static partial class EventCatalog
|| s.Contains("dragon")
|| s.Contains("nuke")
|| s.Contains("excalibur")
|| s.Contains("wonder");
|| s.Contains("wonder")
|| s.Contains("silver")
|| s.Contains("steel")
|| s.Contains("_bone")
|| s.StartsWith("bone_", StringComparison.Ordinal)
|| s.EndsWith("_bone", StringComparison.Ordinal);
}
private static void Ensure()
@ -306,7 +339,12 @@ public static partial class EventCatalog
|| s.Contains("undead")
|| s.Contains("dragon")
|| s.Contains("evolve")
|| s.Contains("mutate");
|| s.Contains("mutate")
|| s.Contains("mutation")
|| s.Contains("death")
|| s.Contains("blood")
|| s.Contains("horror")
|| s.Contains("tentacle");
}
private static void Ensure()
@ -379,10 +417,25 @@ public static partial class EventCatalog
d.AmbientStrength,
signalIds: null,
d.SignalStrength,
signalPredicate: id => id != null && (id.Contains("warfare") || id.Contains("magic") || id.Contains("mutation")),
signalPredicate: IsSignalGene,
ambientCreatesInterest: d.AmbientCreatesInterest);
}
private static bool IsSignalGene(string id)
{
if (string.IsNullOrEmpty(id))
{
return false;
}
string s = id.ToLowerInvariant();
return s.Contains("warfare")
|| s.Contains("magic")
|| s.Contains("mutation")
|| s.Contains("mutagen")
|| s.Contains("damage");
}
public static IEnumerable<string> AuthoredIds
{
get
@ -501,7 +554,17 @@ public static partial class EventCatalog
|| s.Contains("hunger")
|| s.Contains("rebell")
|| s.Contains("disaster")
|| s.Contains("angry");
|| s.Contains("angry")
|| s.Contains("cursed")
|| s.Contains("forever_")
|| s.Contains("tumor")
|| s.Contains("mutant")
|| s.Contains("evolution")
|| s.Contains("old_age")
|| s.Contains("kingdom_expansion")
|| s.Contains("border_steal")
|| s.Contains("civ_army")
|| s.Contains("exploding");
}
public static IEnumerable<string> AuthoredIds
@ -596,7 +659,18 @@ public static partial class EventCatalog
|| s.Contains("zealot")
|| s.Contains("fanatic")
|| s.Contains("schism")
|| s.Contains("royal");
|| s.Contains("royal")
|| s.Contains("conscript")
|| s.Contains("join_or_die")
|| s.Contains("necro")
|| s.Contains("evil")
|| s.Contains("shattered")
|| s.Contains("titan")
|| s.Contains("ethno")
|| s.Contains("flame")
|| s.Contains("ice_weapon")
|| s.Contains("shotgun")
|| s.Contains("blaster");
}
public static class CultureTrait

View file

@ -1994,6 +1994,7 @@ internal static class HarnessScenarios
Step("dea1", "assert", expect: "domain_event_audit"),
Step("dea1b", "assert", expect: "library_asset_labels_ok"),
Step("dea1c", "assert", expect: "library_ambient_policy_ok"),
Step("dea1d", "assert", expect: "camera_a_inventory_ok"),
Step("dea2", "snapshot")
};
}

View file

@ -73,6 +73,131 @@
"id": "warrior_try_join_army_group",
"action": "try to join an army",
"strength": 64
},
{
"id": "warrior_army_leader_move_to_attack_target",
"action": "lead the army to attack",
"strength": 72
},
{
"id": "try_to_start_new_civilization",
"action": "start a new civilization",
"strength": 90
},
{
"id": "build_civ_city_here",
"action": "found a city here",
"strength": 86
},
{
"id": "claim_land",
"action": "claim this land",
"strength": 70
},
{
"id": "king_check_new_city_foundation",
"action": "found a new city",
"strength": 88
},
{
"id": "bee_create_hive",
"action": "create a hive",
"strength": 68
},
{
"id": "sexual_reproduction_try",
"action": "try to reproduce",
"strength": 66
},
{
"id": "asexual_reproduction_budding",
"action": "reproduce by budding",
"strength": 64
},
{
"id": "asexual_reproduction_divine",
"action": "reproduce by divine means",
"strength": 78
},
{
"id": "asexual_reproduction_fission",
"action": "reproduce by fission",
"strength": 64
},
{
"id": "asexual_reproduction_parthenogenesis",
"action": "reproduce by parthenogenesis",
"strength": 66
},
{
"id": "asexual_reproduction_spores",
"action": "reproduce by spores",
"strength": 64
},
{
"id": "asexual_reproduction_vegetative",
"action": "reproduce vegetatively",
"strength": 62
},
{
"id": "family_group_follow",
"action": "follow their family group",
"strength": 52
},
{
"id": "family_group_join_or_new_herd",
"action": "join or form a herd",
"strength": 58
},
{
"id": "family_group_leave",
"action": "leave their family group",
"strength": 56
},
{
"id": "attack_golden_brain",
"action": "attack the golden brain",
"strength": 92
},
{
"id": "make_skeleton",
"action": "raise a skeleton",
"strength": 80
},
{
"id": "status_soul_harvested",
"action": "have their soul harvested",
"strength": 86
},
{
"id": "possessed_following",
"action": "follow under possession",
"strength": 74
},
{
"id": "try_to_take_city_item",
"action": "try to take a city item",
"strength": 66
},
{
"id": "put_out_fire",
"action": "put out a fire",
"strength": 60
},
{
"id": "burn_tumors",
"action": "burn tumors",
"strength": 62
},
{
"id": "try_to_launch_fireworks",
"action": "launch fireworks",
"strength": 58
},
{
"id": "try_affect_dreams",
"action": "affect dreams",
"strength": 56
}
],
"species": [

View file

@ -1,7 +1,7 @@
{
"name": "IdleSpectator",
"author": "dazed",
"version": "0.28.25",
"version": "0.28.27",
"description": "AFK Idle Spectator (I) + Lore (L). Killer button retargets dossier to the killer.",
"GUID": "com.dazed.idlespectator"
}

View file

@ -15,12 +15,14 @@ Authored 1:1 libraries match live counts with zero missing/orphan ids.
Live+dial libraries are fully seeded.
`mutation_gaps.tsv` is empty (`gaps=0`).
The feeling of “lots of events/tips we are not tracking” is mostly **tip quality and Layer A noise**, not missing catalog rows:
Id inventory and interesting Layer A policy are in good shape as of **0.28.27** (`camera_a_inventory`: **219 A / 924 B** on library+decision domains; decisions **37** camera with phrases).
1. Library Labels still interpolate raw `{id}` (`culture gains xenophobic`, `forges armor_leather`).
2. Large ambient-A surfaces (items, genes, phenotypes, subspecies traits, meta traits, world laws) own the camera with generic templates.
3. Building **complete / wonder finish** still has no Layer A wire (destroy/consume only).
4. Meta-trait domains are live-seeded and TSV-audited but **not fail-gated** by `domain_event_audit`.
Remaining feel of thin tips is mostly **soak share / strength vs filler**, not missing catalog rows:
1. Library Labels use display names (Wave 1); keep watching for weak verbs.
2. Ambient CI stays false; Signal predicates own library A (expanded 0.28.27).
3. Building civ complete / wonder A is wired.
4. Meta-trait domains are fail-gated by `domain_event_audit` + `camera_a_inventory_ok`.
Tips here means watch tip / orange reason Labels from interest feeds, not dossier chip hover tooltips.
@ -60,27 +62,32 @@ Seeded via `LiveLibraryInterest.EnsureSeeded` + JSON `libraries.*` dials.
| Domain | Live | Camera A | Dial label template | Notes | Priority |
|--------|-----:|---------:|---------------------|-------|----------|
| decisions | 127 | 12 | `{a} decides to {action}` | 12 camera ids have action phrases; 2 JSON overlays are non-camera (`try_new_plot`, `warrior_train_with_dummy`) | P1 only if new interesting decisions appear |
| spells | 12 | 12 | `{a} casts {id}` | 7 signalIds @86; 5 ambient A @44 | P1 display names; P2 signal set |
| powers | 339 | 23 | `God power: {id}` | ambient CI=false; spectacle predicate | P1 display names |
| items | 111 | 111 | `{a} forges {id}` | ambient A; “forges” weak for armor/non-craft | P1 tip quality |
| genes | 47 | 47 | `{a} gains gene {id}` | ambient A | P1 names; P2 demote ambient spam |
| phenotypes | 52 | 52 | `{a} shows {id}` | ambient A | P1 names; P2 demote ambient spam |
| world_laws | 49 | 49 | `Law changed: {id}` | ambient A | P1 names |
| subspecies_traits | 204 | 204 | `{a} evolves {id}` | ambient A | P1 names; P2 Signal heuristics |
| decisions | 127 | **37** | `{a} decides to {action}` | Signal tokens + authored phrases; fluff/`try_new_plot` B; dump `.harness/camera-a-inventory.tsv` | P3 expanded 0.28.27 |
| spells | 12 | 6 | `{a} casts {id}` | Signal spectacle only; FX spells B | P3 |
| powers | 339 | **41** | `God power: {id}` | ambient CI=false; widened spectacle + creature powers; UI `_edit` B | P3 expanded 0.28.27 |
| items | 111 | **42** | `{a} gains {id}` | Signal mythril/adamantine + silver/steel/bone; leather/iron B | P3 expanded 0.28.27 |
| genes | 47 | **7** | `{a} gains gene {id}` | warfare/damage/mutagen Signal | P3 expanded 0.28.27 |
| phenotypes | 52 | 0 | `{a} shows {id}` | all B | P3 |
| world_laws | 49 | **17** | `Law changed: {id}` | dramatic + forever_/cursed/army/expansion… | P3 expanded 0.28.27 |
| subspecies_traits | 204 | **21** | `{a} evolves {id}` | dramatic + mutation/death/blood/horror | P3 expanded 0.28.27 |
| biomes | 29 | 0 | `Biome shifts: {id}` | ambient CI=false | P3 stay B |
| culture_traits | 78 | 78 | `{a} culture gains {id}` | ~14 dramatic @74; rest ambient @34 | P1 names; hygiene gate |
| religion_traits | 40 | 40 | `{a} faith gains {id}` | dramatic heuristic | P1 names; hygiene gate |
| clan_traits | 29 | 29 | `{a} clan gains {id}` | dramatic heuristic | P1 names; hygiene gate |
| language_traits | 26 | 26 | `{a} tongue gains {id}` | dramatic heuristic | P1 names; hygiene gate |
| culture_traits | 78 | **27** | `{a} culture gains {id}` | dramatic + conscript/necro/craft weapons… | P3 expanded 0.28.27 |
| religion_traits | 40 | 6 | `{a} faith gains {id}` | dramatic heuristic | P3 |
| clan_traits | 29 | 13 | `{a} clan gains {id}` | dramatic heuristic | P3 |
| language_traits | 26 | 2 | `{a} tongue gains {id}` | dramatic heuristic | P3 |
Decision camera set (all have authored action phrases):
`try_to_steal_money`, `kill_unruly_clan_members`, `banish_unruly_clan_members`, `find_lover`,
`king_change_kingdom_{language,culture,religion}`, `leader_change_city_{language,culture,religion}`,
`warrior_try_join_army_group`, `warrior_army_follow_leader`.
**Library+decision Layer A inventory (0.28.27):** live dump total **219 A / 924 B** across 13 domains (`camera_a_inventory_ok`).
Spell signalIds (strength 86): `summon_lightning`, `summon_tornado`, `cast_curse`, `cast_fire`, `cast_blood_rain`, `summon_meteor`, `teleport`.
Ambient-A spells @44: `cast_silence`, `cast_grass_seeds`, `spawn_vegetation`, `spawn_skeleton`, `cast_shield`, `cast_cure`.
### Decision camera classes (0.28.27)
Allow-token Signal after fluff filter (not 127 ambient).
Authored action phrases required for every A id (`domain_event_audit` decision_prose).
Classes: founding/territory (`civilization`, `civ_city`, `claim_land`, `city_foundation`), reproduction/pack (`reproduction`, `family_group`), army/war/politics (existing), spectacle/dark (`golden_brain`, `make_skeleton`, `soul_harvest`, `possessed`), civic soft (`put_out_fire`, `burn_tumors`, `fireworks`, `affect_dreams`), hive (`create_hive`).
Stay B: `try_new_plot*`, diet/sleep/random/check ticks, `warrior_train_with_dummy`, boat trade/fish, house/job/tax chores.
Spell Signal: spectacle summons/curses/fire/teleport/meteor; FX silence/shield/cure/grass/vegetation/skeleton stay B.
## Patch-only / no catalog rows
@ -156,6 +163,14 @@ Status (2026-07-17): **implemented** in mod `0.28.14`.
3. `Building.completeConstruction` wires Layer A for `Building_Civ` (temple/statue spectacle strength bump).
4. `domain_event_audit` fail-gates culture/religion/clan/language trait diffs; `library_asset_labels_ok` dumps `.harness/library-asset-labels.tsv`.
### Interesting A expansion (Phases 02)
Status (2026-07-17): **implemented** in mod `0.28.27`.
1. `camera_a_inventory_ok` dumps `.harness/camera-a-inventory.tsv` (nested in `domain_event_audit`).
2. Decisions: fluff filter refined; Signal classes for founding/reproduction/pack/spectacle; **37** camera A with authored phrases.
3. Library Signal widened (power/item/gene/worldLaw/subspecies/meta); ambient CI stays false; phenotypes/biomes stay 0 A.
### Wave 2 - Signal / ambient A tuning
Status (2026-07-17): **implemented** in mod `0.28.16`.

View file

@ -9,44 +9,21 @@ traits 116 authored 0 116 wired authored_labels P3
disasters 17 authored 0 via_worldlog wired authored_labels P3 disaster_war_audit PASS
war_types_library 5 authored 0 dials wired authored_labels P3
relationship 11 authored_discrete 0 11 wired authored_labels P3 no AssetManager lib
decisions_library 127 live+dial 0 12 wired 12_action_phrases P1 try_new_plot and warrior_train_with_dummy authored non-camera
spells 12 live+dial 0 12 wired raw_id_in_label P1 7 signalIds@86; 5 ambient@44
powers 339 live+dial 0 23 wired raw_id_in_label P1 ambient CI=false
items 111 live+dial 0 111 wired raw_id_forges_verb P1 ambient A; forge verb weak
genes 47 live+dial 0 47 wired raw_id_in_label P1/P2 ambient A spam risk
phenotypes 52 live+dial 0 52 wired raw_id_in_label P1/P2 ambient A spam risk
world_laws_library 49 live+dial 0 49 wired raw_id_in_label P1/P2 ambient A
subspecies_traits 204 live+dial 0 204 wired raw_id_in_label P1/P2 ambient A
biome_library 29 live+dial 0 0 wired raw_id_template P3 ambient CI=false
culture_traits 78 live+dial 0 78 wired raw_id_in_label P1 TSV audited; not fail-gated
religion_traits 40 live+dial 0 40 wired raw_id_in_label P1 TSV audited; not fail-gated
clan_traits 29 live+dial 0 29 wired raw_id_in_label P1 TSV audited; not fail-gated
language_traits 26 live+dial 0 26 wired raw_id_in_label P1 TSV audited; not fail-gated
decisions_library 127 live+dial 0 37 wired 37_action_phrases P3 expanded 0.28.27; camera-a-inventory.tsv
spells 12 live+dial 0 6 wired display_names P3 Signal spectacle; 6 FX B
powers 339 live+dial 0 41 wired display_names P3 Signal widened 0.28.27; ambient CI=false
items 111 live+dial 0 42 wired gains_verb P3 Signal mythril/adamantine/silver/steel/bone
genes 47 live+dial 0 7 wired display_names P3 warfare/damage/mutagen Signal
phenotypes 52 live+dial 0 0 wired display_names P3 all B
world_laws_library 49 live+dial 0 17 wired display_names P3 dramatic + forever_/cursed/army…
subspecies_traits 204 live+dial 0 21 wired display_names P3 dramatic + mutation/death/blood
biome_library 29 live+dial 0 0 wired display_names P3 ambient CI=false
culture_traits 78 live+dial 0 27 wired display_names P3 dramatic widened 0.28.27
religion_traits 40 live+dial 0 6 wired display_names P3
clan_traits 29 live+dial 0 13 wired display_names P3
language_traits 26 live+dial 0 2 wired display_names P3
buildings 618 patch_only n/a consume_and_civ_complete partial EventReason.BuildingEat_BuildingComplete P3 civ complete/wonder A; destroy demoted B
boats n/a patch_only n/a trade_unload wired EventReason.Boat P3 load skipped
combat n/a csharp_policy n/a pair_keys wired EventReason.Fight P3
architecture_library 53 none n/a 0 covered_by_building_activity n/a P3
city_build_orders 3 none n/a 0 covered_by_building_activity n/a P3
kingdoms_traits 5 none n/a 0 covered_by_trait_meta_feeds n/a P3
items_modifiers 59 none n/a 0 covered_by_item_feed n/a P3
combat_action_library 11 none n/a 0 covered_by_spell_combat_feeds n/a P2 only if distinct beats
loyalty_library 29 none n/a 0 covered_by_worldlog_happiness n/a P3
opinion_library 24 none n/a 0 covered_by_worldlog_happiness n/a P3
knowledge_library 11 none n/a 0 covered_by_worldlog_happiness n/a P3
communication_library 10 none n/a 0 covered_by_worldlog_happiness n/a P3
story_library 1 none n/a 0 covered_by_worldlog_happiness n/a P3
citizen_job_library 13 none n/a 0 covered_by_activity_dossier n/a P3
professions 5 none n/a 0 covered_by_activity_dossier n/a P3
personalities 5 none n/a 0 covered_by_activity_dossier n/a P3
tasks_actor 217 none n/a 0 activity_taxonomy n/a P3 unmappedTasks=0
actor_library 322 scoring_overlay n/a 0 species_discovery n/a P3 taxonomy PASS
plot_category_library 9 none n/a 0 category_only n/a P3
AllianceManager.removeObject n/a worldlog 0 via_alliance_dissolved worldlog_covers authored_worldlog P3 not mutation gap
KingdomManager.removeObject n/a worldlog 0 via_kingdom_destroyed worldlog_covers authored_worldlog P3 not mutation gap
building_complete_wonder n/a patch n/a civ_complete Building.completeConstruction EventReason.BuildingComplete P3 temple/statue spectacle strength
World.era_manager_poll n/a wired n/a era_change WorldAgeManager.startNextAge EventCatalog.Era P3 update→startNextAge already emits
World.earthquake_manager_poll n/a wired n/a quake_sticky Earthquake.startQuake EarthquakeActive P3 holds while isQuakeActive
combat_action_library 11 covered_by_spell_combat n/a 0 CombatActionLibrary.tryToCastSpell n/a P3 no separate catalog
story_knowledge_opinion n/a covered_by_worldlog_happiness n/a 0 n/a n/a P3 Wave3 closed
personalities_professions 5+5 covered_by_activity_dossier n/a 0 n/a n/a P3 Wave3 closed
camera_a_inventory 1143 harness 0 219 wired camera-a-inventory.tsv P3 A=219 B=924 domains=13; gate camera_a_inventory_ok
mutation_gaps 0 harness 0 n/a gaps_empty n/a P3 managers=57 libraryAssets=2893

1 library_or_hook live_count catalog_mode missing_ids camera_a wire_status tip_quality priority notes
9 disasters 17 authored 0 via_worldlog wired authored_labels P3 disaster_war_audit PASS
10 war_types_library 5 authored 0 dials wired authored_labels P3
11 relationship 11 authored_discrete 0 11 wired authored_labels P3 no AssetManager lib
12 decisions_library 127 live+dial 0 12 37 wired 12_action_phrases 37_action_phrases P1 P3 try_new_plot and warrior_train_with_dummy authored non-camera expanded 0.28.27; camera-a-inventory.tsv
13 spells 12 live+dial 0 12 6 wired raw_id_in_label display_names P1 P3 7 signalIds@86; 5 ambient@44 Signal spectacle; 6 FX B
14 powers 339 live+dial 0 23 41 wired raw_id_in_label display_names P1 P3 ambient CI=false Signal widened 0.28.27; ambient CI=false
15 items 111 live+dial 0 111 42 wired raw_id_forges_verb gains_verb P1 P3 ambient A; forge verb weak Signal mythril/adamantine/silver/steel/bone
16 genes 47 live+dial 0 47 7 wired raw_id_in_label display_names P1/P2 P3 ambient A spam risk warfare/damage/mutagen Signal
17 phenotypes 52 live+dial 0 52 0 wired raw_id_in_label display_names P1/P2 P3 ambient A spam risk all B
18 world_laws_library 49 live+dial 0 49 17 wired raw_id_in_label display_names P1/P2 P3 ambient A dramatic + forever_/cursed/army…
19 subspecies_traits 204 live+dial 0 204 21 wired raw_id_in_label display_names P1/P2 P3 ambient A dramatic + mutation/death/blood
20 biome_library 29 live+dial 0 0 wired raw_id_template display_names P3 ambient CI=false
21 culture_traits 78 live+dial 0 78 27 wired raw_id_in_label display_names P1 P3 TSV audited; not fail-gated dramatic widened 0.28.27
22 religion_traits 40 live+dial 0 40 6 wired raw_id_in_label display_names P1 P3 TSV audited; not fail-gated
23 clan_traits 29 live+dial 0 29 13 wired raw_id_in_label display_names P1 P3 TSV audited; not fail-gated
24 language_traits 26 live+dial 0 26 2 wired raw_id_in_label display_names P1 P3 TSV audited; not fail-gated
25 buildings 618 patch_only n/a consume_and_civ_complete partial EventReason.BuildingEat_BuildingComplete P3 civ complete/wonder A; destroy demoted B
26 boats n/a patch_only n/a trade_unload wired EventReason.Boat P3 load skipped
27 combat n/a csharp_policy n/a pair_keys wired EventReason.Fight P3
28 architecture_library camera_a_inventory 53 1143 none harness n/a 0 0 219 covered_by_building_activity wired n/a camera-a-inventory.tsv P3 A=219 B=924 domains=13; gate camera_a_inventory_ok
city_build_orders 3 none n/a 0 covered_by_building_activity n/a P3
kingdoms_traits 5 none n/a 0 covered_by_trait_meta_feeds n/a P3
items_modifiers 59 none n/a 0 covered_by_item_feed n/a P3
combat_action_library 11 none n/a 0 covered_by_spell_combat_feeds n/a P2 only if distinct beats
loyalty_library 29 none n/a 0 covered_by_worldlog_happiness n/a P3
opinion_library 24 none n/a 0 covered_by_worldlog_happiness n/a P3
knowledge_library 11 none n/a 0 covered_by_worldlog_happiness n/a P3
communication_library 10 none n/a 0 covered_by_worldlog_happiness n/a P3
story_library 1 none n/a 0 covered_by_worldlog_happiness n/a P3
citizen_job_library 13 none n/a 0 covered_by_activity_dossier n/a P3
professions 5 none n/a 0 covered_by_activity_dossier n/a P3
personalities 5 none n/a 0 covered_by_activity_dossier n/a P3
tasks_actor 217 none n/a 0 activity_taxonomy n/a P3 unmappedTasks=0
actor_library 322 scoring_overlay n/a 0 species_discovery n/a P3 taxonomy PASS
plot_category_library 9 none n/a 0 category_only n/a P3
AllianceManager.removeObject n/a worldlog 0 via_alliance_dissolved worldlog_covers authored_worldlog P3 not mutation gap
KingdomManager.removeObject n/a worldlog 0 via_kingdom_destroyed worldlog_covers authored_worldlog P3 not mutation gap
building_complete_wonder n/a patch n/a civ_complete Building.completeConstruction EventReason.BuildingComplete P3 temple/statue spectacle strength
World.era_manager_poll n/a wired n/a era_change WorldAgeManager.startNextAge EventCatalog.Era P3 update→startNextAge already emits
World.earthquake_manager_poll n/a wired n/a quake_sticky Earthquake.startQuake EarthquakeActive P3 holds while isQuakeActive
combat_action_library 11 covered_by_spell_combat n/a 0 CombatActionLibrary.tryToCastSpell n/a P3 no separate catalog
story_knowledge_opinion n/a covered_by_worldlog_happiness n/a 0 n/a n/a P3 Wave3 closed
personalities_professions 5+5 covered_by_activity_dossier n/a 0 n/a n/a P3 Wave3 closed
29 mutation_gaps 0 harness 0 n/a gaps_empty n/a P3 managers=57 libraryAssets=2893

View file

@ -52,10 +52,11 @@ Only true brief FX, cooldowns, civic Aggregates, and god-tool / biome spam stay
| Relationship | 11 | all interesting discrete |
| Plot / Era / War / Book | 28 / 11 / 5 / 12 | all A |
| Trait | 116 | all CI |
| Decision | 127 live / 14 prose overlays | 12 camera-interesting |
| Spell / Item / Gene / Phenotype / WorldLaw / SubspeciesTrait | live | Signal-only A (ambient CI=false); phenotypes all B |
| Culture / Religion / Clan / LanguageTrait | live dials | Signal-only via dramatic heuristic |
| Power / Biome | live | Signal predicate only / ambient CI=false |
| Decision | 127 live / 37 prose overlays | **37** camera-interesting (0.28.27) |
| Spell / Item / Gene / Phenotype / WorldLaw / SubspeciesTrait | live | Signal-only A: spell 6 / item 42 / gene 7 / phenotype 0 / law 17 / subsp 21 |
| Culture / Religion / Clan / LanguageTrait | live dials | Signal dramatic: 27 / 6 / 13 / 2 |
| Power / Biome | live | Power Signal **41**; biome 0 (ambient CI=false) |
| Library+decision inventory dump | 1143 rows | **219 A / 924 B** (`.harness/camera-a-inventory.tsv`) |
| Building / Boat | none | destroy/consume/trade/unload; damage/load skipped |
## Wired hooks (major)