systems/FACTION_STANDING_WIRING_SPEC.md
Status: ACTIVE (2026-08-04). The engine-consumable half of the faction-standing mint. The
executable half is harness/emit_faction_registry.py and harness/apply_ladder_rewards.py; the data
is four tables. This doc is what an engine lane reads to wire readers and writers off real rows.
The fun-gap wave-2 audit found the nine-band standing ladder ruled and its struct generated, and then
found nothing underneath it: the ladder registry held two rows with a blank reward reference, 160 of
164 cast rows carried 176 distinct free-text faction tokens with no table to point at, the two
worldstate rows named consumers that resolve to nothing, and **only the negative direction of
community memory was specced** — the corpus could record being hurt and could not record being
helped.
Josh's bar, verbatim: *"trades and skills and reputations and factions and exploration like World of
Warcraft."* A world that only remembers harm is not that world. So the grammar below carries both
signs, and the positive side is the larger one: twenty-four positive classes against sixteen harm
classes, every one of them lifted from a line canon had already written and nobody had wired.
DERIVED FROM:
_source/02_Tier_2_Region_Pages/*.md § Section 8 "Politics Territories and Faction Relationships"— the *Five-Factors faction table* on all twelve slice region pages. Every faction row is minted
from one bullet and cites its line: flores_island.md:L812-L821, bali.md:L1854-L1870,
sumatra_java.md:L1787-L1794, angkor_khmer.md:L622-L625, sri_lanka.md:L838-L858,
south_india.md:L713-L732, swahili_coast.md:L735-L755, ethiopia.md:L655-L672,
rift_valley.md:L576-L590, central_africa_congo.md:L939-L958, west_africa.md:L2162-L2193,
south_africa.md:L63,L67,L573.
_source/01_Tier_1_Foundation/T1_Integrity_Paths_Worldstates_Master [ACTIVE v2.1].md §8.11.1 (thenine ruled bands and the STANDING label list), §8.11.4 (write discipline: qualitative
class-and-direction tokens, never point values), §8.7 (faction standing never hard-gates
progression; relationships are locally real and not symmetrically modeled), §4.9 (accumulation),
L192 (a kill ripples through the community and is remembered).
registries/T0_Character_Index [ACTIVE v1.1] — the faction-token census, script-emitted from thelive CSV on every run, never hand-typed.
registries/T0_Festival_Registry [DRAFT v0.1] — the nine rows whose own hooks already say*standing*: FST_0003, FST_0004, FST_0005, FST_0008, FST_0010, FST_0011, FST_0013, FST_0014,
FST_0016. Derived by reading the registry, with a positive control that fails the emit if the set
drifts from the routing table.
docs/spine/CH_05.md L194/L196/L206, CH_08.md L131/L185, CH_12.md L190/L193, CH_13.md L267 —the eight standing writes the slice already authored in prose.
docs/proposals/systems/attackability-and-consequence-model.md § The five attackability tiers and§ The engine — the harm side's
existing doctrine (witness, memory, community resilience, the escalation ladder), which the
negative half of the grammar references rather than re-invents.
NOT DERIVED (authored judgment, and why it had no canon home):
"closed but courteous"); the nine bands are numeric. Something had to map one onto the other. The
rule is stated, deterministic, unit-tested, and every row carries the page's own phrase beside the
derived band so the mapping is auditable against source rather than trusted.
enumerate reward classes. The table only re-expresses cited improve classes as offers and adds
nothing that no page named.
T0_Faction_Registry — the table the 176 tokens now point at67 rows, 22 columns. registries/T0_Faction_Registry [DRAFT v0.1]/Sheet1.csv.
| column | UE type | notes |
|---|---|---|
faction_id | FName | FAC_\d{4}, RowName |
faction_name | FText | player-facing; localization-bearing |
kind | UENUM + FString mirror | community · institution · order · network · apparatus |
region_ref | FDataTableRowHandle | → T0_Region_Index.region_id |
chapter_ref | FName | CH_NN |
relationship_type | FString | the page's own token, carried verbatim |
standing_on_arrival | UENUM + mirror | a STANDING label, or NOT_APPLICABLE |
arrival_note | FString | the region page's own phrase — the evidence for the derived band |
web_ally_refs / web_rival_refs / web_trade_refs | FString + parse helper | ;-joined FAC_ ids |
improve_event_classes / degrade_event_classes | FString + parse helper | ;-joined SE_ ids |
never_target | UENUM + mirror | TRUE (inviolate individual and collective) · COLLECTIVE (the culture is protected; individual members are Tier-2 attackable) · FALSE |
joinable | bool | |
questline_ref | FString | designer prose, the page's own |
cast_token_count | int32 | how many T0_Character_Index rows re-point here |
ladder_ref | FName | GLOBAL_DEFAULT_9BAND |
reveal_gated / reveal_gate_anchor | bool / FName | |
source_ref | FString | file:line |
notes | FString | designer prose |
Applied by derive_band(); unit-tested with must-fire and must-not-fire controls.
Stranger (Neutral band).
No region page states an arrival above Friendly, and the emitter asserts it: **Honored, Revered and
Exalted are earned, never granted.** That is the whole shape of the WoW ladder Josh named.
Ch 38, and T0_Faction_Registry is read by slice surfaces. T0_Reputation_Ladder_Registry
FR_0002 is reveal_gated post-Ch-38 and its faction_ref is therefore **blank by the firewall,
not by omission** — the row says so on its own face.
else; the cross-chapter join lives in the antagonist registries where the gating already exists.
T0_Standing_Event_Grammar — one vocabulary, both signs40 rows, 12 columns: 24 positive, 16 negative.
registries/T0_Standing_Event_Grammar [DRAFT v0.1]/Sheet1.csv.
| column | UE type | notes |
|---|---|---|
event_class_id | FName | SE_\d{4}, RowName |
event_class | FName | internal routing vocabulary; never rendered |
sign | UENUM | positive · negative |
direction | UENUM | the ratified fork vocabulary: generative · neutral · corrosive |
family | UENUM + mirror | solidarity protection provision gift healing rite festival labor knowledge trade restraint restitution record bearing coercion allocation corruption theft extraction harm desecration |
player_verb | FText | what the player does, in a person's words |
world_reads | FText | what the world does back — a person treating you differently, never a bar filling |
magnitude_class | FString | BLANK on every row. §8.11.4 rules relational movement writes to be qualitative class-and-direction tokens; per-beat rates are runtime clamps. Phase-5M fills this or nothing does |
applies_to_kind | FString + parse | which faction kinds the class can fire on |
first_wired_writers | FString + parse | the rows that fire it *today* |
authority_ref | FString | the cited canon line the class was lifted from |
Every class below cites a clause canon had already written. Josh's four named examples are the first
four checked by the emitter's self-test, so they cannot silently rot out:
SE_0002 defended_the_ground (secure the violated edge / secure the biome), SE_0003 freed_the_held (freeing the captives raises standing *because the community is the
refuge that receives them*), SE_0022 exposed_the_apparatus_cleanly.
SE_0004 provisioned_the_feast (the nado mere precedence feast, the Nage buffalorite, the Batak sacrifice — the shared table as a standing surface).
SE_0005 gave_a_gift, whose canonical home is the Vedda community, where *presentsand reciprocity matter in a way the ethnography is explicit about*.
SE_0008 carried_a_festival_role and SE_0009 contested_in_the_ring(the caci circuit, the kalari pit — climb for community standing, offered never forced).
And the ones the corpus had written and never wired: SE_0006 held_the_ward_through_the_night,
SE_0010 completed_the_work (a terrace block through a full cycle; a forge shift), `SE_0011
took_the_place_assigned (the net line and the division of the kill), SE_0013
returned_what_belongs_to_the_keepers, SE_0014 restrained_from_reaching` — the Baka Jengi gate,
where the region's most protected content is opened by not reaching for it, which is the single
best positive-standing mechanic already sitting in canon.
The harm classes were already doctrine and are now rows on the same ladder rather than a separate
system: SE_0040 read_need_as_leverage, SE_0041 took_more_than_the_share, `SE_0043
stepped_the_line_for_a_private_take (the band's own documented offence), SE_0044
treated_knowledge_as_inventory, SE_0051 participated_in_the_extraction, SE_0052
killed_a_community_member, SE_0054 attempted_systematic_slaughter` — which routes to the escalation
ladder (settlement → region → hunted-across-the-world) and to the Nemesis band's display label,
Hunted. The culture endures and is never erasable; the player becomes the hunted party. That is
the ladder and the anti-genocide floor meeting at one row.
Two rows carry an empty degrade column *by canon, not by omission*: FAC_0059 (the Agojie — no degrade
path routes through violence, HL_0003) and FAC_0044/FAC_0049, where canon stages no beat at all.
T0_Standing_Reward_Band and the ladder9 rows. Rewards are classes, and every one is an OFFER: vendor access, escort, teaching, shelter,
festival roles, work contracts, introductions, kit and lore at Exalted. Every row carries the §8.7
no-dead-end clause in its own non_gating_clause column — **standing shapes what you are handed,
never what you can reach.** Nothing on this ladder gates a route; a closed opportunity re-routes to an
equivalent-class alternative.
T0_Reputation_Ladder_Registry, 2 rows → 52exalted_reward_ref → SB_0009, plus faction_ref, band_reward_ref, offer_classes, non_gating_note and notes. FR_0001's pre-faction-table
label ("Swahili coast trade-guild") now resolves onto the collective the region page itself names,
the ocean web (FAC_0037). Its trade_off_pair_partner and asymmetric_bridge_ref stay blank on
purpose and say why on the row: §8.7 rules faction relationships locally real and **not
symmetrically modeled**, and no Section 8 bullet declares an opposed pair — an authored one would
be invention.
tier_thresholds is **copied verbatim fromFR_0001 at runtime, never retyped**, so a per-faction override (a deliberate act under §8.11.4)
cannot happen by accident. offer_classes is derived from that faction's own cited improve
classes through the family → offer table: knowledge → teaching, labor → work_contract, trade →
vendor_access, festival/rite → festival_role, solidarity → escort, protection/healing → shelter,
gift/restraint/restitution → lore, record/bearing → introduction. A community offers what the
region page says you earn from it.
This is the section an engine lane wires off. Left column is the beat or surface; right is the event
class that fires.
| beat / fork option | old free-text token | now | class |
|---|---|---|---|
CH_05 FORK_CH05_A generative (CH_05.md:L194) | temple_household + | FAC_0018 | SE_0001 |
CH_05 FORK_CH05_A corrosive (L196) | court − | FAC_0020 | SE_0040 |
CH_05 FORK_CH05_B generative (L206-L207) | samosir_community + | FAC_0010 | SE_0001 |
CH_08 CH08_B## reef (CH_08.md:L131) | +waganga_warding_trust | FAC_0038 | SE_0002 |
CH_08 FORK_CH08_B generative (L185) | kilwa_sufi_community standing rises | FAC_0033 | SE_0003 |
CH_12 FORK_CH12_A generative (CH_12.md:L190) | ogboni_resistance + | FAC_0061 | SE_0021 |
CH_12 FORK_CH12_A corrosive (L193) | community − | FAC_0064 | SE_0040 |
CH_13 FORK_CH13_A both directions (CH_13.md:L267) | the mine labour-record mark | FAC_0066 / FAC_0067 | SE_0021 / SE_0051 |
These are already-authored, already-standing-bearing content that had nothing to write into. They are
the cheapest positive surface in the slice and they are all opt-in.
| festival | region · chapter | class |
|---|---|---|
| FST_0003 uma randang, the field-opening | flores · CH_03 | SE_0008, SE_0012 |
| FST_0004 the caci contest season | flores · CH_03 | SE_0009 |
| FST_0005 nado mere, feast precedence | flores · CH_02-03 | SE_0007, SE_0004, and SE_0050 when the claim sours the room |
| FST_0008 the banten-tegeh temple festival | bali · CH_04 | SE_0007 |
| FST_0010 canang sari, the daily offering | bali · CH_04 | SE_0007 |
| FST_0011 Galungan | bali · CH_04 | SE_0008, SE_0004 |
| FST_0013 the bius rite | sumatra_java · CH_05 | SE_0007 |
| FST_0014 the buffalo sacrifice and porbuhitan augury | sumatra_java · CH_05 | SE_0004, SE_0007 |
| FST_0016 the Thap Ba Po Nagar festival | angkor_khmer · CH_05 | SE_0008, SE_0015 |
The routing carries a positive control: the emitter re-derives the standing-bearing set from the
festival registry's own text on every run and refuses to emit if it no longer matches this table.
Chapters 2, 3, 4, 6, 7, 9, 10 and 11 author zero faction-standing writes today. Their factions,
improve classes and ladder rows now exist, so the writes are authorable rather than blocked — this is
a worklist for the quest and dialogue lanes, not a defect in this mint. The highest-value four, each
already sitting in canon and needing only a beat to fire from:
is a written mechanic with no write.
Both cells were replaced under an old-value guard in harness/apply_ladder_rewards.py.
faction_standing_per_faction → T0_Faction_Registry.faction_id (the key domain), T0_Standing_Event_Grammar (the write vocabulary, both signs), T0_Standing_Reward_Band (the read
side), T0_Reputation_Ladder_Registry.faction_ref, and §5 of this doc.
reputation_tier_per_faction → T0_Reputation_Ladder_Registry.band_reward_ref → T0_Standing_Reward_Band.reward_band_id, and §5 of this doc.
docs/faction_token_map.csv carries all 176 tokens with a disposition and a reason. 87 tokens across
88 cast rows RESOLVED. The rest are declared:
a director call, not a lane guess: manggarai_adjacent_hunter_lineage_corrupted_individual and
batak_ritual_corrupted_lineage_pustaha_dark_practice both name a lineage *and* mark their bearer
as a corrupt departure from it, so resolving them seats a chapter antagonist inside a protected-host
community, and minting a corrupted-lineage faction asserts the lineage carries the corruption —
which sumatra_java.md:L1790 denies in terms ("the office is honored and the corrupt individual
does not define it"). Two are Ch-47/48 living-institution rows awaiting their region page and a
§17.1 care read. Two are fairy-realm rows the ruled slice order designs last.
Solange Diallo); south_india.md:L732 rules the Siddha tradition out by name; sri_lanka.md:L858
states the counterfeit "is not a community, has no faction, no summoner and no apparatus".
destination is named on every row: that region's page Section 8 faction table, at which point
re-running the emitter re-points them.
Magnitudes stay blank behind Phase-5M by rule, not by omission. The grammar names classes and
directions; the numbers are runtime clamps.
python harness/emit_faction_registry.py --selftest # 23 checks incl. 3 negative controls
python harness/apply_ladder_rewards.py --selftest # 7 checks
python harness/emit_faction_registry.py --apply
python harness/apply_ladder_rewards.py --apply # idempotent; re-running is a no-op
python harness/registry_fidelity.py --reference _source/00_Tier_0_Master_Indices \
--candidate registries --fk-spec docs/fk_spec.json \
--baseline docs/fidelity_baseline.json --out harness/scorecard \
--emit-baseline docs/fidelity_baseline.json
python harness/rag/index.py --scope canon # the canon hash moved; index_freshness reads it
python harness/run_gates.py # UNPIPED
Idempotency is proven, not asserted. Re-running the emitter after the re-point has landed
produces a byte-identical docs/faction_token_map.csv. The first cut did not: the census read
the cells it had itself rewritten, saw 132 FAC_ ids where 176 tokens had been, and would have
rewritten the map to nonsense while reporting a clean run. The census now skips already-canonical
cells and merges the prior map — which is the only surviving record of what a re-pointed cell used
to say — and an accounting control fails loudly if a FAC_ cell exists that no RESOLVED row
claims.
Gate state at landing. 46 gates PASS. Two fail and neither is this lane's, both introduced
by commit 604a6624 and reproducible on an untouched checkout: scan reports a
lowercase_bullet at _source/01_Tier_1_Foundation/T1_Personal_Dimension_Spec [ACTIVE v1.1].md:277
in a file this lane never opened (git diff HEAD -- _source/ is empty), and pillar10_vocab names
harness/check_pillar10_vocab.py, which git cat-file -e HEAD:harness/check_pillar10_vocab.py
proves does not exist at HEAD — a committed gate config pointing at an uncommitted script.