pipelines/LANE_engine-build-infra.md
Verbatim from workflow wf_d0ca605f-891. Each finding carries either its own register entry id or
the cross-lane merge that absorbed it. This file is the evidence of record the compact register
entries cite.
docs/REGISTRY_ROW_STRUCT_SPEC.md §5 L210-217 records the debt verbatim: "62 registry CSVs against 48 generated headers (19 stale, 23 absent). Do NOT run gen_row_struct.py --all --apply until that backlog is reconciled deliberately."C:/dev/humanity-forgotten/registries/ = 62 registry dirs / 62 CSVs; C:/dev/Humanity/Humanity/Source/Humanity/Public/Data/*.h = 48 row structs; Content/Data/Registries/DT_*.uasset = 46 real DataTables (+DT_ImportSmoke). Name-normalized diff → 23 registries with NO row struct, carrying 1,787 rows: Arc_Grid 1231, Promise_Ledger 125, Arc_Index 128, Element_Effectiveness 124, Composure_Routing 53, Festival 16, Ally_Behavior_Policy 14, Rarity_Grade 13, Bonus_Option 12, Realm_Ruleset 11, Status_Table 10, Bonus_Pool 7, Role_Axis 7, Arc_Grammar 6, Spec_Purpose 6, Dungeon_Class_Reference 5, Familiar_Bond_Ability 5, Imprint_Affix 5, Role_Composition_Rule 4, Loadout_Set 3, Equipment 1, Motif 1, Boss_Attack_Rail 0. The invisibility is structural: Tools/registry_import_manifest.json _doc states "The registry entries come from line 2 (// Source:) of each Source/Humanity/Public/Data/*.h header" — count=46, entries=46. The import pipeline's denominator IS the headers, so a registry with no header is never reported missing; it reports 46/46 green.gen_row_struct --check is wired into harness/gates_config.json over --all, the new gate goes red on 19 stale + 23 absent, not the 13 it was scoped for — the gate cannot land green without a decision it does not own. Then ranks 13-17, which freeze schemas onto registries that have no engine consumer to validate the freeze against.docs/PRE_5090_BUILD_PLAN.md ▶ WINDOW PLAN [engine-code·L] between ranks 2 and 13: "Reconcile the 62-vs-48 row-struct backlog — per-registry review + generate + import + manifest entry for the 23 absent, then promote the bulk --all --apply exit to non-zero (the second named debt in REGISTRY_ROW_STRUCT_SPEC §5)." Coverage tooth: extend gen_row_struct --check --all to enumerate registries, not headers.Source/Humanity/Public/Quest/GeneratedWorldstateSchema.h L7-8 states the contract: "Each ws.* variable also gets a native gameplay tag \"Humanity.WS.<variable_name>\" so the durable UWorldStateSubsystem (FGameplayTag-keyed) is addressable by variable name with no hand authoring." T1_Build_Pipeline_Contracts [ACTIVE v1.0] §9.5 (quoted in harness/check_ws_value_form.py docstring): "ws.{name} is DURABLE and must resolve to a WS_NNN registry variable_name." docs/DOC_MAP.md L112 (LOADOUT_SETS_SPEC, Wave-3, double-critic-cleared) declares "per-owner homing (WS_045 player / WS_027 companion / WS_012 familiar)".registries/T0_Worldstate_Variables [ACTIVE v0.1]/Sheet1.csv = 46 rows (WS_001–WS_046). GeneratedWorldstateSchema.h L2 self-declares "(42 rows at generation)" and contains exactly 42 UE_DECLARE_GAMEPLAY_TAG_EXTERN lines, last = WS_banked_tell_reads (WS_042). So WS_043 bonus_selection_state, WS_044 bonus_respec_ledger, WS_045 loadout_set_state, WS_046 item_instance_ledger have NO native tag and NO baked schema row — TagForVar() returns an invalid tag for all four. Their declared consumers are named engine classes that EXIST: WS_045's consumers cell reads "vril_site_interaction (HumanityVrilRechargeZone)" and Source/.../HumanityVrilRechargeZone.h/.cpp are on disk. POSITIVE CONTROL on the tooling zero: gen_worldstate_schema returns 0 hits across the entire canon repo (no DOC_MAP row, no gates_config entry, no runbook), while gen_row_struct returns 53 hits across 15 files — the search space is real and the generator is simply unregistered. harness/gates_config.json has 34 gates; ws_value_form validates VALUE heads against domains, nothing validates that a key is addressable.ws.item_instance_ledger is already written once by the spine (docs/spine/CH_01.md:271).gen_worldstate_schema.py in docs/DOC_MAP.md alongside gen_row_struct.py, and add a worldstate_schema gate to harness/gates_config.json that fails when the registry row count / variable set diverges from the generated header (the same shape as rank 2's gen_row_struct --check wiring — bundle the two into one rank so the trunk gets ONE cross-repo drift tooth, not two half ones).T1_Build_Pipeline_Contracts [ACTIVE v1.0] §1.8 L209-214 rules the protocol in full: "Once a build ships, player save files exist in the wild at the schema version of the shipping build… Migration protocol operates on schema_version_introduced metadata"; "schema_version_introduced — required column on T0_Worldstate_Variables for every variable… formatted as semver per §1.11"; "For variables whose schema_version_introduced is newer than the saved schema_version, save load populates the variable with default_value from T0_Worldstate_Variables. For variables removed from current schema, save load strips the variable with deprecation warning."T0_Worldstate_Variables/Sheet1.csv (46 rows): schema_version_introduced is populated 46/46 but 11 rows carry the literal string v0.2 — not semver, in direct violation of the §1.11 formatting §1.8 requires (WS_029-032, WS_033-039). default is populated 36/46 — 10 rows have NO default at all (WS_029, 030, 031, 032, 034, 035, 036, 037, 038, 039), so the ruled "populate with default_value" step has nothing to read for those. Engine side: the generated FWsVarDef struct (GeneratedWorldstateSchema.h L69-77) carries Name / Kind / Members / bHasRange / Lo / Hi — it drops BOTH default and schema_version_introduced. WorldStateSaveGame.h carries a single int32 SaveVersion = 1 and the migration exists only as a comment: "SaveVersion is carried so a future schema migration can detect and up-convert an old slot rather than mis-read it." There is no migration function, no per-var default repopulation, no strip-with-warning. POSITIVE CONTROL: searching the docs tree for SaveVersion / save migration / up-convert / backward-compat returns nothing on-point while migrat returns dozens of unrelated hits (5090_SETUP_RUNBOOK soak migration, CASCADE_RUNBOOK schema migration notes) — the search could match.docs/ROADMAP_POST_5090_TO_SHIP.md L441 rules the EA base as the full 14-node slice, and L503 rules that "each finished chapter (or chapter batch) ships as a Steam content update through the P4 chain" — every one of those updates is a ws.* schema bump against in-the-wild saves.v0.2 values to real semver and author the 10 missing defaults (the domain column already states every one of their shapes). (2) A docs/proposals/systems/SAVE_MIGRATION_SPEC.md in the Wave-3 spec family, plus the generator change to emit Default + SchemaVersionIntroduced into FWsVarDef and a SchemaVersion field on UWorldStateSaveGame.docs/ROADMAP_POST_5090_TO_SHIP.md P4 L416-421 names the machinery by reference: "Shipping-config builds and the cook/package/validation chain (PRE_5090_BUILD_PLAN.md W4.10 the RunUAT BuildCookRun headless per-region cook + -run=DataValidation; W4.9 the RowName + reveal-ladder + hard-line cook-time validators)"; P4 exit L451: "the Shipping build cooks clean with all DataValidation validators green"; L465-467: "launch-cert QA runs on the real Shipping build, not the editor, because the Shipping config is where a latent non-Shipping-module link would surface (the FunctionalTesting Developer-module lesson)."PRE_5090_BUILD_PLAN.md §6 (L2872-2873) — and that document's own header L34-48 declares §§3–11 SUPERSEDED as the item source: "For item detail + wave assignment going forward, read Vol2, not §§3–11 here." In PRE_5090_BUILD_PLAN_VOL2.md, W4.9 and W4.10 appear only as dependencies of other items (QT-8 L1463 "W4.9 UEditorValidatorBase", QT-22 L1477 deps "W4.10, QA-8", W8/W11 handshake verified-by L315/L1538) — never as item rows with a wave. POSITIVE CONTROL: sibling W4.x items DID get Vol2 folds — W4.1 → RG.1 L1135, W4.8 → §F′ RM.1/RM.2 L1177-1183 — so the pattern exists and W4.9/W4.10 were the two that fell out. Config side, Config/DefaultGame.ini is 11 lines: CommonUISettings, three ConsoleVariables, ProjectID. No [/Script/UnrealEd.ProjectPackagingSettings], no ProjectVersion, no maps-to-cook list, no chunking; GameDefaultMap=/Game/Maps/GreyboxArena (a dev map) is what a packaged build would boot. POSITIVE CONTROL: ProjectPackagingSettings / ProjectVersion / bGenerateChunks return 0 hits across docs/ while the same sweep hits analytics elsewhere. Binaries: Binaries/Win64/ contains only HumanityEditor.target + UnrealEditor-*.dll/pdb — no Humanity.target, no game executable; Saved/Cooked and Saved/StagedBuilds do not exist.Humanity.Build.cs does guard FunctionalTesting/AutomationController behind Target.Configuration != Shipping, so the discipline is there — but nothing has ever tested it, which is precisely the failure class the ROADMAP quotes as its own lesson.PRE_5090_BUILD_PLAN_VOL2.md §8 (QA-ORG) with a wave, and add a window-plan rank [engine-code·M] "Build the standalone Game target (Development) + author ProjectPackagingSettings/ProjectVersion + a boot-map that is not GreyboxArena" — the boot-flow UI half already has a home at Vol2 U11.6.docs/ROADMAP_POST_5090_TO_SHIP.md P4 exit L453: "a day-0 patch path exists and runs through the bug ledger." L503: "each finished chapter (or chapter batch) ships as a Steam content update through the P4 chain." T1_Build_Pipeline_Contracts §1.11 rules SCHEMA version discipline (semver) and is cited by nine downstream sections — the discipline exists for canon, and the same doc's §1.8 L214 requires "explicit migration handling specified per-bump in build pipeline configuration alongside the §1.11 MAJOR-version-mismatch halt protocol."ProjectVersion = 0 hits in Config/DefaultGame.ini and 0 hits in docs/. No patch/hotfix lane exists in either plan document (the P4 line is the only mention and it is an exit assertion, not an item). Crash/symbol posture: CrashReport / crash report / GDPR / privacy policy / opt-in telemetry / data collection all return 0 hits across docs/ — POSITIVE-CONTROLLED, because telemetry returns 138 hits across 35 files in the same tree. What telemetry exists is dev-facing only: UCombatTelemetrySubsystem → Saved/Telemetry/<s>.json and Saved/Crashes/**/CrashContext.runtime-xml ingestion, and its plan item QT-14 (PRE_5090_BUILD_PLAN_VOL2.md L1416) is explicitly the internal auto-triage loop: "Ingest Saved/Logs/*.log + Saved/Crashes/**/CrashContext.runtime-xml… auto-open a typed record." No symbol/PDB retention policy per shipped build exists — and Binaries/ is gitignored per the game CLAUDE.md L92-96, so the 88 MB editor PDB currently on disk is not archived anywhere.docs/MARKETING_WEB_PROGRAM.md's sibling lane already did the privacy read for the website (Cloudflare analytics, PIPE_WEBSITE_2026-07-29 L20) — the GAME-side player-data posture was never asked.docs/SHIP_BUILD_CONTRACT.md (sibling of docs/ASSET_DROPIN_CONTRACT.md, DOC_MAP-rowed) owning: build-version scheme + where it is stamped (ProjectVersion → save header → crash context), per-build PDB/symbol archive location, the patch/hotfix lane, and the shipped-build crash-report + player-opt-in posture. Ruled at director level per the care/decision protocol, not left to the build lane.CLAUDE.md L97-101 pins the plugin discipline ("Plugins/NeoStackAI is installed AS SOURCE and compiled against this exact engine build") and L110-131 documents both agent channels as live editor automation: NeoStack MCP on 127.0.0.1:9315 and Epic's ModelContextProtocol on 127.0.0.1:8000, the latter "auto-started via the repo-tracked seed Config/DefaultEditorPerProjectUserSettings.ini (bAutoStartServer=True)." docs/ROADMAP_POST_5090_TO_SHIP.md L465-467 requires launch-cert QA on the real Shipping build.Humanity.uproject enables 10 plugins. Exactly ONE carries a target fence: ModelingToolsEditorMode has "TargetAllowList": ["Editor"]. NeoStackAI (an ACP agent bridge that spawns claude.exe per CLAUDE.md L152-161) declares only SupportedTargetPlatforms: [Win64, Mac, Linux] — a platform list, not a target fence. ModelContextProtocol declares nothing at all, and its settings section is /Script/ModelContextProtocolEngine.* — an Engine/Runtime module name, not an Editor one. POSITIVE CONTROL on the doc zero: TargetAllowList returns 0 hits across the whole canon docs/ tree, while the same regex's NeoStack-ship/package alternates return 15 hits across 9 files — the concept has never been raised, not merely not-found. And no packaging config exists to strip them (see the ProjectPackagingSettings zero above).SHIP_BUILD_CONTRACT.md above: the declared shipped plugin/module allow-list, with TargetAllowList: ["Editor"] applied to NeoStackAI + ModelContextProtocol, plus a cook-time validator row alongside W4.9's RowName validator that fails the cook on an unexpected staged module.verify-with-fanout-not-commit-labels: "'class closed' + green gates still missed 7 stragglers." Memory csv-width-defects-invisible-to-suite: duplicate headers and short rows "pass every gate and misread silently." The whole gate apparatus exists because a claim without a tooth rots; the canon→engine seam is the one claim with no tooth.harness/gates_config.json = 34 gates, enumerated: stress, fidelity, scan, topography, layer_register, lint, anchors, graph, no_dead_end, boss_axes, spine_graph, lifecycle, reveal_discipline, registry_extensions, index_freshness, store_integrity, region_pages, ws_value_form, slice_coherence, seed_housing, build_readiness, entity_taxonomy, care_scope, grand_sage_silence, weave_lifecycle, build_space, rarity_firewall, counter_class_coverage, ally_build, factory_contract, realm_ruleset, arc_continuity, promise_ledger, emotional_curve. Every one reads canon-repo paths only — build_readiness (the closest candidate) reads region-PAGE machine-field presence per its own docstring ("For each region page it emits one readiness row per downstream CONSUMER LANE"), never the game repo. Not one gate reads C:/dev/Humanity/Humanity/Source/ or Content/Data/Registries/. POSITIVE CONTROL: gen_row_struct is cited 53 times across 15 canon docs yet appears in ZERO gates_config entries — the tool the trunk depends on has never been armed.harness/check_canon_engine_trunk.py gate asserting all four hops (registry↔struct, struct↔DataTable, registry↔manifest entry, ws.* row↔native tag) with the game repo read-only, and add it to gates_config.json. It must land BEFORE gap 1's reconciliation rank so the reconciliation has a scorecard to close against.anti-orphan-wire-everything-doc-map: "every artifact needs a consumer AND a DOC_MAP row." docs/ASSET_DROPIN_CONTRACT.md L120 makes the id-rail the load-bearing claim: "It carries a canon id — the stable-id UPROPERTY equals its registry RowName (the W4.9 DataValidation pass)." The trunk's premise is that importing a DataTable makes canon reachable.Content/Data/Registries/. Grepping all 201 files under Source/ for /Game/Data/Registries/DT_* returns 17 distinct paths: AbilityTree, Beat, BossEncounter, BossPhase, Clue, CreatureRoster, ElementMatrix, Flora, Fork, ForkOption, GatherNode, HollowTrickPalette, Mineral, SceneAnchor, Sfx, Site, VrilSite. Three of those (Clue, ElementMatrix, HollowTrickPalette) point at assets that do NOT exist — all three are declared, null-safe and TODO_CANON-flagged with embedded fallbacks (HumanityClueSubsystem.cpp:48-50, HumanityElementMatrix.cpp:54), so they are honest debt, not defects. That leaves 14 imported tables with a C++ consumer and 32 with none — including DT_ChapterIndex (79 rows), DT_RegionIndex, DT_QuestDefinition, DT_Weapon, DT_CrossCulturalLinkGraph (145), DT_ThreadGrid, DT_Rune, DT_SceneSpec, DT_HardLines, DT_WorldstateVariables. HONEST LIMIT, stated because the rule demands it: I could NOT prove the Blueprint direction — a binary name-table scan of all 262 non-DT uassets for DT_ChapterIndex returned zero, but so did the control scan for DT_BossPhase (a table known to be C++-referenced), so that search CANNOT match and its zero proves nothing. The claim is "no C++ consumer," not "no consumer."AssetRegistry dependency query, not a text scan — the text scan is proven unable to match). Report, don't fail, at v1: the honest register is the deliverable, the same shape as check_build_readiness.py's BLOCKED-ON matrix.CLAUDE.md L11-15 is unambiguous: "The canon repo… READ-ONLY from this lane. Never edit anything there. If you find a canon defect or need a value that doesn't exist, record it in TODO_CANON.md… never invent the value yourself." docs/REGISTRY_ROW_STRUCT_SPEC.md §1 (quoted in §6): "the canon repo owns the rows," and §6 rejects a generated shadow CSV precisely because "the shadow copy becomes a second thing that can be edited, imported stale, or diverge."Tools/DT_ElementMatrix.csv (373 B) and Tools/DT_HollowTrickPalette.csv (876 B). Both are honestly self-declared — ElementMatrixRow.h L2-8: "Engine-Authored: Tools/DT_ElementMatrix.csv… whose CSV is a game-repo DERIVED file, not a canon registry"; HumanityHollowTrickPalette.h L24: "DATA HOME (flagged TODO_CANON, the ElementMatrix precedent): no canon-side registry for the trick palette exists yet — T0_Hollow_Codex carries no trick columns and the HOLLOW EXPANSION INTEGRATION record's own worklist still has the mint open." The structural admission is in the manifest: registry_import_manifest.json carries engine_authored: 2. Element matrix HAS an owner — window-plan rank 17 "[schema-data·M] Complete + import the element matrix; lift the 10-element cap" — and a partial canon home (T0_Element_Effectiveness 124 rows, though its shape does not match ElementMatrixRow.h's attacker-row/10-column form). The hollow trick palette has NEITHER: no registry directory exists for it (POSITIVE CONTROL: T0_Hollow_Codex [DRAFT v0.1] does exist as a registry dir with a row struct and a DT, so the search space is real) and no rank in the 30-item queue or the R-31/R-32 additions names it.hollow-class-expansion-rulings — six axes, two tiers) and the Phase-5M build-space sweeps, which sweep pool options against canon rows.T0_Hollow_Trick_Palette, then retire Tools/DT_HollowTrickPalette.csv to a generated import and drop engine_authored from 2 to 1 — with rank 17 closing the last one.docs/COMBAT_PROGRAM_ADDENDUM.md §12 L256-263 carries Josh's verbatim intent: "this also sets us up well for progression into MMO after the full game is released" — "the same role/stat/build architecture scales into Tier-C" raid roles and the canonical 100-player World Boss (T1_Tier_C_MMO_Spec §8; CVD §13.1). docs/ENGINE_OPTIMIZATION_DOCTRINE.md L495-496 calls it "the single most important CPU decision for our shape, because canon carries Tier-C raid roles and a 100-player World Boss."PRE_5090_BUILD_PLAN.md §12 item 27: "Tier-C MMO scope ruling — the base game's netcode posture (single-player-first? always-online? how the MMO layer attaches). GAS OnRep hooks imply a latent decision" — and §13 deliberately parks the build: "Not-model-gated but SEQUENCE-gated: Tier-C MMO netcode + guild-integrity aggregation + world bosses — last of the Tier A→B→C build; the math is specifiable now but building it is premature." So the engine SPEC is correctly not a gap. What is unstructured is the cost record: T0_Worldstate_Variables has 46 rows and 10 columns (variable_id, variable_name, type, domain, default, consumers, canonical_authority, schema_version_introduced, notes, extensions) — none declares server-authority or replication scope. Engine side, replication exists only where GAS supplies it (HumanityAttributeSet.cpp 20 DOREPLIFETIME lines, HumanityWorldDemandAttributeSet.cpp 4); no actor sets bReplicates, there is no server RPC, no OnlineSubsystem module (all 9 "Steam" hits in Source are the elemental Steam of the 10-element matrix, not Steamworks), and UWorldStateSubsystem is a UGameInstanceSubsystem — client-local by construction.golden-reenrich-run-step0-coverage-check warns about.T0_Worldstate_Variables — authority_scope (client_local | server_authoritative | undeclared) — added in the same edit as gap 3's semver/default fix, defaulting to undeclared so nothing is invented and item 27's ruling has a home to land in. Record it against §12 item 27 rather than minting a new decision.