# WWW Revision Log

2026-09-15 Combat DEMO profile-generation queue terminal reconciliation repair: the hourly worker ticked a specific LONG request but then `reconcile()` inspected the globally latest active request, so DATASET `REJECTED` was not persisted to that LONG row and it remained locally `QUEUED`, repeatedly blocking the fresh horizon pack. `DatasetProfileGenerationRequest::reconcile()` now accepts an optional exact request ID; `ProfileGenerationQueueWorker` passes its ticked ID. Existing callers remain compatible. Production backups: `20260915_102036Z_120622ade556`, `20260915_102056Z_0a67ec8c548a`. Deployed hashes: request class `8c3ed568fab095d28312fc3c3cc10539675315d07ab073c8992fe02c5758a57b`; worker `88ec5895c9cad90813e70ce921229fc99b92dbe581a8efabe5809bfdc8267299`. First natural tick was `COMPLETE`: stuck LONG became `REJECTED`, active count 1494→1493, and processing advanced to the next request. LONG DEMO execution itself remained healthy; no trading-rule or LIVE mutation.


2026-09-09 DATASET tenant SHADOW migration / operational access: canonical project root is `/var/www/tredercopis/data/www`; canonical CLI PHP for maintenance, migration, lint and manual verification is `/opt/php84/bin/php` (PHP 8.4). The shell-default `php` may be an older system runtime and must not be treated as the project compatibility reference. Guarded workspace access does not expose arbitrary shell execution, so operator-run PHP commands must be complete and explicitly use the canonical binary. Legacy shared SHADOW evidence migration `dataset_shadow_evidence_migration_v1` ran with `apply=true`: 3 exactly-owned profiles migrated into `side/shadow_league/engines/<engine_id>/strategies/<strategy_id>/profiles/`; 8 were safely skipped with `NO_LEGACY_EVIDENCE`. Migrated evidence: LONG HSCAN 12H (3 hours, 3 resolved, -30 forward PnL), SHORT HSCAN 4H (2 hours, 5 resolved, +7.3170965285), SHORT SHOTR4 4H (15 hours, 193 resolved, +198.8223242108). Legacy rank/champion were not migrated; evidence migration does not enable LIVE or Combat publication.

2026-09-09 generated Library ownership backfill applied for proven records only: 11 existing immutable DATASET Library profiles (8 SHORT, 3 LONG) with `status=COMPLETED`, exact `library_profile_id`, and authenticated generation-request owner were annotated with Engine ID `live_10deb15c9b45ead2ad99c7c9` plus source request provenance. No heuristic assignment was made to the remaining legacy ROLL profiles or any Library entry without an exact completed generation request. Post-write audit confirms 8 SHORT + 3 LONG owner-tagged entries. This makes those 11 profiles eligible for the new per-Engine physical SHADOW league without cross-tenant guessing.

2026-09-09 DATASET SHADOW physical tenant isolation: persistent profile league storage is now scoped by approved Engine ID under `storage/profile_runner/{long|short}/shadow_league/engines/<engine_id>/{state.json,latest.json,profiles/*}`. The DATASET cron enumerates APPROVED Live Engines and evaluates LONG/SHORT independently per Engine using only Library entries whose `engine_id` matches that Engine; snapshot/profile/state documents persist the owner. Combat consumer now reads its Engine-specific snapshot path. This removes cross-Combat evidence/ranking/champion contamination for new owner-tagged Library profiles. Existing unowned legacy Library entries are intentionally excluded from the new physical league until ownership is backfilled/migrated; generated legacy entries remain readable through Combat's request-based owner fallback but do not silently enter a tenant league. Bug audit note: heartbeat progress/return totals were updated from fixed 2 to `approved_engines * 2`. No execution/LIVE mutation enabled.

2026-09-09 Engine-owner isolation hardening + bug audit: Combat `DatasetShadowLeagueConsumer` now requires the local approved Engine ID, filters DATASET generated Library definitions and SHADOW snapshot rows to profiles owned by that Engine, and re-ranks only the tenant-visible rows before exposing them to strategy SHADOW/LIVE-control pages. Legacy generated Library entries without `engine_id` are owner-resolved through their DATASET generation request, preserving current live_01 generated profiles while preventing cross-Engine visibility. Generated DEMO activation now uses the same owner-aware consumer. Also fixed a generation inbox idempotency defect: server-mutated status/receive timestamps could make a legitimate replay of the same request ID compare unequal; the API now hashes the canonical authenticated request and keeps server fields outside `request_original`. DATASET's underlying SHADOW league persistence is still globally stored/evaluated; Combat exposure is tenant-isolated, while physical per-Engine league storage remains follow-up work.

2026-09-09 DATASET generated-profile tenant ownership phase 1: reused the existing approved Combat Engine ID as the canonical profile owner instead of introducing a second tenant identifier. Combat profile-generation submit now embeds its authenticated `engine_id`; DATASET rejects a conflicting claimed owner, persists authenticated ownership on the generation request/challenger, and requires/persists `engine_id` on newly recorded immutable Library entries. Existing status/delete owner checks remain in force. This closes ownership loss on the generation -> challenger -> Library path for new profiles; physical Library namespace/listing/selector isolation is a separate follow-up and must be completed before claiming full multi-Combat isolation. No generator formula/build algorithm, DEMO selector, or LIVE behavior changed.

2026-09-09 Rich Equity Curve phase 2: added Combat-local Shadow overlay plumbing. `/equity-curve/` now discovers local profile-lab resolved events and reconstructs per-profile cumulative 5m virtual PnL series for SHADOW `ALLOW` decisions; available Shadow profiles appear as opt-in toggles and render as dashed comparison curves over the same Combat-local timeline. LONG local forward-filter metadata is also surfaced from this Combat's `long_profile_lab_stats.json` without importing DATASET league accounting. No selector/degradation automation or profile mutation was enabled. Important semantic guard: SHORT profile-lab series is benchmark/virtual PnL, not exchange PnL; UI keeps it explicitly under SHADOW comparison.

2026-09-09 Rich Equity Curve ownership correction: `/equity-curve/` is now explicitly Combat-local and no longer calls/depends on `DatasetApprovalClient` for strategy discovery. Strategy existence/label/side are resolved from this Combat instance's own installed package registry + runtime package manifest; PnL/equity remains reconstructed only from this Combat instance's local ownership/trades/control storage. Therefore N independent Combat installations naturally have N independent curves even if they consume the same DATASET profiles. DATASET may supply profile definitions elsewhere, but it is not a source of Equity Curve accounting.

2026-09-09 Rich Equity Curve phase 1 deployed. Added dedicated Combat page `/equity-curve/` for strategy/env PnL analysis without changing generator or LIVE/DEMO controls. It reconstructs a continuous 5-minute time axis from persisted strategy ownership/trades, shows Total/Realized equity, per-5m trade activity, 1H/3H/6H/12H/24H/3D/7D/ALL windows, current/realized/unrealized/peak/max-DD/trades/WR KPIs, hover detail, and persisted profile-switch marker/history when available. Existing compact curve remains on `/strategy/` and now links to the rich 5m page. This is observability-only; no profile selection/degradation automation was enabled.

2026-09-09 Profile Generator terminal-progress defect CLOSED. The remaining E2E observability issue from `PROFILE_GENERATOR_E2E_2026-09-09` was verified against production code/state: DATASET `generation_worker_v1.php` now normalizes `progress.next_symbol_offset` to `symbols_total` on both terminal `COMPLETED` and `REJECTED` writes, while Combat `profile-generator.php` independently normalizes terminal presentation to 100% after reconciliation. The three protected surviving generated requests were inspected in Combat control storage and all persist `520/520`; the active HSCAN SHORT and LONG requests therefore render as terminal 100% rather than stale `480/520`. This is observability-only; no DEMO/LIVE profile/control mutation was performed during verification. Combined with the completed LONG+SHORT HSCAN lifecycle, MANUAL DEMO cron hold, case-insensitive activation/deletion protection and generated cleanup verification, the Profile Generator E2E completion gate is CLOSED/PASS.

2026-09-09 Generated DEMO selection persistence completed. The earlier case-insensitive DATASET Library fix was verified by the real completed request `GEN_SHORT_20260909_034029_5AA1FB`: generated profile `SHORT_ORIGINAL_12H_20260909_0340_4df972` activated successfully through `DATASET_PROFILE_LIBRARY`, so `GEN_DEMO_PROFILE_NOT_EXECUTION_COMPATIBLE` is closed. A separate follow-up fault was then confirmed: `DatasetChampionControl::applyDemo()` unconditionally restored SHADOW rank #1 on the next master cron tick, making an explicit generated-profile activation last only seconds. Combat now has an explicit per-strategy DEMO selection mode in runtime settings. Generated activation sets `demo_profile_selection_mode=MANUAL` plus `demo_manual_profile_id`; Champion application returns `MANUAL_NO_CHANGE` and preserves that profile. The operator can activate any existing COMPLETED generated profile from Profile Generator, see the exact current DEMO profile/mode, and use `Вернуть Champion AUTO` to resume immediate rank-1 following. Missing mode remains backward-compatible AUTO. `pg_profile_in_use()` now compares IDs case-insensitively so lowercase DATASET filename suffixes cannot bypass deletion protection. LIVE drift hashing/publication excludes the DEMO-only control keys, and neither activation nor AUTO return mutates/promotes LIVE. Changed: `combatsolo.info/engine/strategies/dataset_champion_control.php`, `combatsolo.info/engine/strategies/dataset_generated_demo_activation.php`, `combatsolo.info/public/profile-generator.php`. Verification: PHP 8.4.3 parser PASS on all three files; isolated real-class test PASS for library case-insensitivity, MANUAL activation, cron hold, AUTO Champion switch, persisted AUTO state and `live_untouched`; first natural post-deploy MASTER_CRON tick at 2026-09-09 04:57:53 UTC was `COMPLETE`, with existing LONG/SHORT AUTO profiles `ALREADY_CHAMPION` and no change caused by deployment. Automatic backups/mutation ids were recorded for all three production writes.

2026-09-09 Generated DEMO activation compatibility fix: universal generated profile IDs preserve a lowercase 6-hex suffix in DATASET library filenames, while Combat activation normalizes requested profile IDs to uppercase. Before a generated profile had appeared in the latest SHADOW snapshot, the library fallback therefore looked for an uppercase filename on the case-sensitive filesystem and returned `GEN_DEMO_PROFILE_NOT_EXECUTION_COMPATIBLE` despite a valid immutable library entry. `DatasetShadowLeagueConsumer::executionDefinitionById()` now resolves library filenames case-insensitively by profile ID before validating the entry. DEMO activation only; no LIVE mutation.

2026-09-09 Horizon pack control finalized as one LONG+SHORT tournament action: Profile Generator discovers the installed LONG and SHORT strategies and queues both sides at the same immutable `to_ts`, each across 1/2/3/4/6/12/24h with AUTO + COMPETE + HSCAN. This keeps side comparison aligned and remains browser-driven/SHADOW-only.

2026-09-09 HSCAN package generation finalized as a paired LONG+SHORT tournament. One Profile Generator action now creates 14 immutable AUTO/COMPETE requests at one common `to_ts`: LONG and SHORT each at 1/2/3/4/6/12/24h. This replaces the earlier side-specific package action and is intended to compare freshness horizon symmetrically. First observed LONG HSCAN: 1-2h insufficient causal evidence; 3/4/6h converged to the same 20-row profile (11W/0L, 55% training traffic); 12h had 81 rows, 34W/4L, 46.91% traffic; 24h had 184 rows, 44W/8L, 28.26% traffic. First observed SHORT HSCAN: 3h 30 rows, 9W/0L, 30% traffic; 4h 70 rows, 25W/3L, 40% traffic; 6h 95 rows, 28W/5L, 34.74% traffic; 12h 237 rows, 88W/33L, 51.05% traffic; 24h 608 rows, 89W/44L, 21.88% traffic. Training results are descriptive only; Champion remains governed by common-forward SHADOW evidence and eligibility gates. No direct LIVE mutation.

2026-09-09 Experimental adaptive profile rotation + horizon tournament added. DATASET SHADOW profiles now retain the last six hourly cohort outcomes; ranking marks a profile as confirmed drawdown when its last two cohorts together have >=4 resolved trades and negative forward PnL, temporarily removing it from Champion eligibility. Existing evidence gates remain >=10 resolved, >=3h and >=30% traffic, preventing one-loss profile thrashing. Combat Profile Generator now has a fair AUTO/COMPETE horizon pack (1/2/3/4/6/12/24h) with one immutable common `to_ts`; browser-driven generation processes queued pack members sequentially while the page remains open. Pack label is `HSCAN`; LIVE is never directly mutated.

2026-09-09 Combat strategy cards now expose the exact active entry profile from local runtime control (`long_entry_profile` / `short_entry_profile`) directly inside the selected DEMO/LIVE side card. This makes Champion/profile changes visible without opening strategy internals and does not mutate runtime state.

2026-09-08 SHADOW Champion traffic/evidence gate tightened after SHORT G5 starvation: `FAIR_EVIDENCE_V1` previously allowed rank eligibility at only 5 resolved samples and 2 evaluated hours with no minimum traffic. SHORT G5 therefore became rank-1/DEMO Champion on 9 resolved samples, 2h evidence and 22.5% traffic despite G4 having 81 resolved samples, 4h evidence and 57.45% traffic. Rank eligibility now requires >=10 resolved, >=3 evaluated hours, and >=30% traffic before ranking score can elect a Champion. This changes DATASET SHADOW qualification only; no direct LIVE mutation.

2026-09-08 SHORT generated-profile recognition widened: SHORT DEMO previously classified only `SHORT_ROLL24H_G*` ids as DATASET profiles. Universal-generator ids (`SHORT_<LABEL>_<N>H_<date>_<time>_<hash>`) could therefore fall through to legacy manifest/ORIGINAL handling instead of DATASET structured-rule execution. SHORT runner now recognizes both rolling and universal generated DATASET profile-id families before resolving immutable execution definitions. No LIVE mutation.

2026-09-08 generator lost-request recovery: after generated-profile cleanup, Combat could retain a local QUEUED generator request while DATASET generation_requests was empty; page ticking then failed permanently with `GEN_REQUEST_NOT_FOUND`. `DatasetProfileGenerationRequest::tickRemote()` now treats that exact error as recoverable only when the immutable local request JSON still exists: it re-submits that same request/id/window to DATASET and returns DATASET_QUEUED. Other tick errors remain fail-closed. This does not mutate DEMO/LIVE.

2026-09-08 runtime DATASET profile lookup regression fixed: current DEMO SHORT was receiving real base signals but rejecting them as `ENTRY_SKIPPED_DATASET_PROFILE_MISSING` (latest observed `SHORT_ROLL24H_G4_20260908_1222`), even though that exact immutable profile exists in DATASET Library and current SHADOW snapshot with execution conditions. Root cause: both DEMO runners instantiate `DatasetShadowLeagueConsumer(dirname($this->storageRoot))`; with runtime storage root `<combat>/storage`, this passes `<combat>/storage`, while the consumer assumed it always received `<combat>` and therefore resolved sibling DATASET one directory too high. The consumer constructor now normalizes an incoming path whose basename is `storage` back to the Combat project root before sibling DATASET resolution. This repairs both LONG and SHORT callers centrally and remains read-only with no LIVE mutation. Runtime PHP lint/fresh-signal verification is pending operator execution/natural scan.

## 2026-09-07 — Legacy Combat SHORT SHADOW retired from active runtime/UI

Champion→DEMO AUTO wired into Combat master engine tick (2026-09-08); PHP 8.4 lint PASS for `engine/strategies/dataset_champion_control.php` and `engine/cron/engine_tick.php`. Champion→DEMO AUTO runtime activation PASS (2026-09-08): deterministic `bin/cron.php` tick at 12:24:20+01 completed the Champion settings convergence after the adapter/idempotency fixes. Combat `local_runtime_control` now shows LONG `LONG_ROLL24H_G2_20260908_0709` with risk stop=-30/floor=5/arm=10/gap=5 and compatibility trigger=7.5; SHORT `SHORT_ROLL24H_G2_20260908_0709` with stop=-50/floor=5/arm=10/gap=3 and compatibility trigger=7.5. Both DEMO runners executed successfully (`status=COMPLETE`); SHORT managed 4 already-open positions and did not resnapshot them. Tick overall status was `DEGRADED` only because the LONG DEMO Bybit wallet request timed out once; LONG positions/open-orders calls still succeeded, so this is transport health, not Champion-control failure. LIVE execution wiring remained disabled in the engine tick and no LIVE Champion publication/toggle was performed. Runtime-entry 24h history blocker patched (2026-09-08): both active DEMO runners still use the cheap ~35-bar batch for universe/base-signal scanning, but after a base signal and only when the selected profile is `*_ROLL24H_G*`, they now fetch a 300-bar 5m window before `DatasetStructuredRuleEvaluator`. This supplies `ts-86400` required by `DATASET_CAUSAL_FEATURES_V1` without multiplying wide-history API load across the full universe. Wide-fetch failure remains fail-closed through evaluator `DATA_MISSING`; no fallback permits an entry. PHP 8.4 lint PASS for both patched DEMO runners. First post-patch runtime tick (2026-09-08 12:29:20+01) did NOT exercise the new 300-bar structured-rule path: LONG management was healthy but scan returned `SKIPPED_LATE` at bar 1788866400 with lag=244s (> configured 180s); SHORT management failed on Bybit demo positions timeout and scan returned `SKIPPED_MANAGEMENT_UNHEALTHY`. Therefore no conclusion yet about runtime `DATA_MISSING`/ALLOW/BLOCK behavior from this tick. Champion settings remained G2 on both sides and LIVE wiring remained off; transport degradation is separate. Need a timely healthy scan tick (or a dedicated read-only runner-entry audit) before marking runtime-entry PASS. Added `combatsolo.info/tools/dataset_runtime_entry_readonly_audit.php`: mutation=false gate using the actual installed Combat LONG/SHORT `matchConfirmed5m()` handlers plus the actual `DatasetStructuredRuleEvaluator` against real local DATASET Parser2 confirmed bars and current rank-1 SHADOW rules. It reports base_signals/complete/ALLOW/BLOCK/DATA_MISSING and requires every actual Combat base signal in the audited cohort to have a complete structured-rule evaluation with zero DATA_MISSING. This deliberately avoids Bybit transport and order submission; PHP8.4 lint + audit execution PASS (2026-09-08): `combat_dataset_runtime_entry_readonly_audit_v1` returned ok=true, mutation=false on 80 real Parser2 symbols per side using the actual installed Combat matchers. LONG G2: 6 base signals, 6 complete evaluations, 3 ALLOW / 3 BLOCK / 0 DATA_MISSING. SHORT G2: 88 base signals, 88 complete evaluations, 36 ALLOW / 52 BLOCK / 0 DATA_MISSING. This closes the deterministic runtime-entry data/structured-rule gate for current G2 LONG+SHORT; it does not claim Bybit transport/order-submit parity. LIVE MANUAL/AUTO hardening started: `DatasetChampionControl::plan()` AUTO drift detection now compares Champion profile ID plus immutable profile-definition hash, settings hash, package version and package SHA, not profile ID alone. Thus AUTO can create a new revision when execution semantics/package drift even under the same profile ID; MANUAL still reports no auto-switch. `LivePromotionControl::publish()` preserves `operator_live_enabled`, so AUTO publication does not toggle LIVE ON/OFF. Added read-only `tools/dataset_live_auto_state_machine_audit.php`, which hashes the real control file before/after, inspects both sides, asserts MANUAL never requests an AUTO switch, and performs no state mutation. Read-only state-machine audit PASS: real control SHA unchanged before/after (`d81ad57b...76926`); LONG MANUAL/OFF revision1 G1 vs Champion G2 and SHORT MANUAL/ON revision3 G1 vs Champion G2 both reported `auto_switch_required=false`, proving MANUAL suppression with current real state. Runtime wiring then added in `engine/cron/engine_tick.php`: immediately before each LIVE mirror runner, `applyLiveAuto()` is invoked and its result exposed under `execution.live.champion_auto`. In MANUAL it is a no-op (`MANUAL_NO_CHANGE`); in AUTO it publishes a new immutable revision on Champion/profile-definition/settings/package drift while preserving `operator_live_enabled`, then the LIVE mirror sees the converged revision. This wiring intentionally does not toggle LIVE ON/OFF. Existing SHORT MANUAL+ON therefore must remain G1 on the first verification tick. PHP8.4 lint PASS for LIVE AUTO engine wiring. First runtime verification attempt returned engine tick `LOCKED`, so the newly wired `execution.live.champion_auto` branch did not execute and remains runtime-unverified. The subsequent read-only state-machine audit again PASSed with identical real control SHA before/after (`d81ad57b...76926`): LONG remains MANUAL/OFF revision1 G1 and SHORT remains MANUAL/ON revision3 G1 while DATASET Champion is G2; `auto_switch_required=false` for both. Runtime wiring PASS on the subsequent natural MASTER_CRON tick (`last_tick.json`, 2026-09-08T12:47:31+01:00): tick COMPLETE/ok=true and `execution.live.champion_auto` is present. SHORT reported MANUAL, operator_live_enabled=true, current G1, target Champion G2, auto_switch_required=false, `live_status=MANUAL_NO_CHANGE`; LONG reported MANUAL, operator_live_enabled=false, current G1, target G2, auto_switch_required=false and the same MANUAL no-change semantics. The real LIVE control SHA remained unchanged in the follow-up audit. This closes the runtime MANUAL suppression/wiring gate. AUTO mutation behavior itself is not tested by changing the real control state. Added isolated temporary-storage audit `tools/live_promotion_auto_isolated_audit.php`: it exercises the real `LivePromotionControl` class outside production storage and asserts MANUAL state remains unchanged, AUTO+OFF publication increments revision while remaining OFF, and AUTO+ON package-drift publication increments revision while remaining ON and retaining AUTO. Important contract clarification: raw `publishAutoChampion()` intentionally publishes every time it is called; idempotency is provided by `DatasetChampionControl::plan()/applyLiveAuto()` drift guard, so full end-to-end AUTO idempotency still needs a ChampionControl isolated harness. PHP8.4 lint + isolated AUTO audit PASS. Results: MANUAL revision stayed 1→1; AUTO+OFF published Champion G2 revision 1→2 and preserved LIVE OFF; AUTO+ON package drift on same G2 published revision 2→3 and preserved LIVE ON with package v2. `production_mutation=false`. Immediate production read-only audit also PASSed with identical SHA before/after (`d81ad57b25fd90aef60f4afa010ccb9a5b61f383700311812dd2b5642cd76926`): LONG remains MANUAL/OFF rev1 G1, SHORT remains MANUAL/ON rev3 G1. Raw `publishAutoChampion()` mutation semantics are therefore runtime-proven in isolation; full `DatasetChampionControl::applyLiveAuto()` idempotency/drift behavior is now covered by new isolated harness `tools/dataset_champion_auto_isolated_audit.php` and PHP8.4 runtime PASS. It creates fake sibling `combatsolo.info`/`datasetis.info` roots under `/tmp`, uses the real DATASET snapshot consumer, StrategyRuntimeControl, DatasetChampionControl and LivePromotionControl, and asserts: AUTO+OFF Champion switch increments one revision and preserves OFF; unchanged next tick is `ALREADY_CHAMPION` with no revision; AUTO+ON package drift increments one revision and preserves ON; unchanged next tick is idempotent; same-profile definition drift increments one revision; same-profile runtime-settings drift increments one revision; MANUAL suppresses a later Champion-definition change with no revision. No production storage path is passed to the harness.

2026-09-08 runner DATASET consumer root audit: confirmed a real path bug in both actual entry runners. With runtime `$storageRoot=<combat>/storage`, they instantiated `DatasetShadowLeagueConsumer(dirname(dirname($this->storageRoot)))`, yielding the parent of Combat; the consumer then applies another `dirname()` and therefore searched one directory too high for sibling `datasetis.info`. Both SHORT and LONG runners are patched to `DatasetShadowLeagueConsumer(dirname($this->storageRoot))`, which supplies the actual Combat project root expected by the consumer. This is an entry-path bug fix only; no LIVE control/state mutation. PHP8.4 lint and real-data read-only recheck PASS. Direct consumer resolution after the root fix: LONG G2 resolved YES with 2 structured rules; SHORT G2 resolved YES with 3 structured rules. `dataset_runtime_entry_readonly_audit.php` PASS on first deterministic 80 real Parser2 symbols with mutation=false: LONG base=6, complete=6, allow=3, block=3, missing=0; SHORT base=91, complete=91, allow=36, block=55, missing=0. The SHORT base count differs from the earlier 88 because the underlying real Parser2 history advanced; completeness remains 100%. Follow-up LIVE state-machine audit remained mutation=false with identical control SHA before/after (`d81ad57b25fd90aef60f4afa010ccb9a5b61f383700311812dd2b5642cd76926`), LONG MANUAL/OFF rev1 G1 and SHORT MANUAL/ON rev3 G1. Runner DATASET consumer root blocker is CLOSED.

2026-09-08 DATASET entry traceability hardening: actual LONG/SHORT DEMO runners now persist the successful DATASET structured-rule gate into each new owner snapshot as `dataset_entry_evaluation`. For DATASET rolling profiles it contains immutable-at-entry `profile_id`, `DatasetStructuredRuleEvaluator::CONTRACT`, explicit `decision=ALLOW`, `complete`, `allow`, causal feature values, evaluator reasons, and the exact `structured_rules` used for the decision. SHORT carries this field through ENTRY_SUBMITTING, POSITION_PENDING, and OPEN owner variants; LONG carries it in its intent, which is retained into pending/open owner state. Legacy/non-DATASET entries retain an empty field. This does not change entry eligibility/risk and does not mutate existing positions or LIVE control. PHP8.4 lint/runtime read-only verification PASS: both modified runners lint clean; `dataset_runtime_entry_readonly_audit.php` remains mutation=false with LONG base=6/complete=6/allow=3/block=3/missing=0 and SHORT base=91/complete=91/allow=36/block=55/missing=0. Follow-up LIVE state-machine audit remains mutation=false with unchanged production control SHA `d81ad57b25fd90aef60f4afa010ccb9a5b61f383700311812dd2b5642cd76926`. Entry traceability code is therefore syntax/parity safe; persistence will become directly observable on the next newly opened DATASET owner (existing owners are intentionally not rewritten).

2026-09-08 SHORT DATASET wide-kline dedup: after a DATASET base signal, the 300-bar `kline5m` response fetched for `DatasetStructuredRuleEvaluator` is now retained and reused by the later `formulaV2Filter` path instead of issuing a second identical 300-bar request for the same symbol/bar. Non-DATASET behavior is unchanged and still fetches its own formula history. DATASET failure semantics remain unchanged: if the shared wide request fails, evaluator/formula paths retain their existing incomplete/fail-close handling. This is a transport/load optimization only; entry rules and risk are unchanged. PHP8.4 lint/read-only parity verification PASS: SHORT runner lint clean; real DATASET Parser2 audit remains mutation=false with LONG 6/6 complete (3 allow, 3 block) and SHORT 91/91 complete (36 allow, 55 block), zero data-missing. LIVE state-machine audit also remains mutation=false with production control SHA unchanged at `d81ad57b25fd90aef60f4afa010ccb9a5b61f383700311812dd2b5642cd76926`. SHORT DATASET wide-kline dedup is CLOSED.

2026-09-08 immutable DATASET library fallback for manual/execution profile lookup: `DatasetShadowLeagueConsumer::executionDefinitionById()` still prefers the current persistent SHADOW snapshot, but if a requested profile is absent from `shadow_league/latest.json` it now performs a read-only lookup of the exact immutable DATASET library artifact `<dataset>/storage/profile_runner/<side>/library/<PROFILE_ID>.json`. Fallback is fail-closed: side must be LONG/SHORT, profile id is path-safe, schema must be `dataset_profile_library_entry_v1`, side/profile must match, `immutable=true`, and conditions must pass the existing execution-definition compatibility gate. Returned fallback definitions are marked `source=DATASET_PROFILE_LIBRARY`, `status=DATASET_SHADOW_LIBRARY`, `dataset_shadow_rank=0`, `dataset_library_fallback=true`. This prevents a valid historical library profile from becoming unresolvable merely because it is absent from the latest league snapshot; no DATASET writes or LIVE control mutation. PHP8.4 lint + current-snapshot/path-guard verification PASS: consumer lint clean; LONG/SHORT G1 resolve from `DATASET_SHADOW_LEAGUE`; traversal probe `../../etc/passwd` returns no definition (`BAD_PATH=PASS`); real Parser2 entry audit remains mutation=false (LONG 6/6 complete, SHORT 91/91 complete); LIVE control SHA remains unchanged. The actual immutable-library fallback branch still requires an isolated snapshot-absent harness before this item is fully CLOSED. Added `tools/dataset_library_fallback_isolated_audit.php`: it builds fake sibling Combat/DATASET roots under `/tmp`, intentionally creates no SHADOW latest snapshot, writes immutable LONG/SHORT library fixtures, resolves them through the real consumer, verifies `DATASET_PROFILE_LIBRARY`/fallback markers, rejects traversal and missing IDs, deletes the temp tree, and reports `production_mutation=false`. PHP8.4 runtime execution PASS. Isolated snapshot-absent fallback audit returned `ok=true`, `production_mutation=false`, LONG and SHORT both resolved from `DATASET_PROFILE_LIBRARY` with `fallback=true`, traversal blocked, missing profile blocked. Follow-up production LIVE state-machine audit returned mutation=false and identical control SHA before/after (`d81ad57b25fd90aef60f4afa010ccb9a5b61f383700311812dd2b5642cd76926`). Immutable DATASET library fallback is CLOSED.

2026-09-08 LIVE mirror semantics audit: `LiveMirrorRunner` does NOT independently scan market signals or evaluate DATASET structured rules. It reads already-created DEMO ownership/trade rows (`sourceRows()`), and when LIVE submit is enabled it mirrors a recent DEMO `OPEN` row only after publication-time/lag and `matchesPromotion()` checks. Therefore the intended DATASET entry gate is upstream in DEMO; LIVE is a downstream execution mirror. Critical defect found for rolling LONG profiles: `matchesPromotion()` hard-codes LONG acceptance to `profile_id === ORIGINAL_LONG_F4_R50_B2`, so a published `LONG_ROLL24H_G*` profile can never mirror a DEMO LONG rolling-profile entry. SHORT compares `src.short_entry_profile` to published profile and is compatible. Also, LIVE mirror ownership currently records promotion hashes/revision but not the DEMO `dataset_entry_evaluation` proof. LONG match semantics and proof propagation were immediately patched: LONG now compares `src.long_entry_profile` to the published promotion profile exactly, matching SHORT semantics; `newMirrorRow()` now copies the upstream immutable-at-entry `dataset_entry_evaluation` proof into LIVE mirror ownership. This does not add a second market evaluator in LIVE—the source DEMO entry remains the single DATASET gate—and does not mutate current LIVE control or existing positions. PHP8.4 lint + existing real-data/safety regression checks PASS: `live_mirror_runner.php` lint clean; DATASET runtime-entry audit remains mutation=false with LONG 6/6 complete (3 allow/3 block) and SHORT 91/91 complete (36 allow/55 block); production LIVE state-machine remains mutation=false with identical control SHA `d81ad57b25fd90aef60f4afa010ccb9a5b61f383700311812dd2b5642cd76926`. Added deterministic no-submit harness `tools/dataset_live_mirror_semantics_audit.php`. `LiveMirrorRunner` exposes two side-effect-free audit helpers backed by the same internal exact-match implementation used by runtime: exact LONG/SHORT profile matching and extraction of the upstream DATASET proof. The harness asserts LONG exact match, LONG wrong-profile reject, SHORT exact match, SHORT wrong-profile reject, proof preservation, and empty proof for legacy source; it performs no Bybit submit and no production mutation. Existing post-patch regression execution PASS: `live_mirror_runner.php` lint clean; real DATASET runtime-entry audit remains mutation=false (LONG 6/6 complete, SHORT 91/91 complete); production LIVE state-machine remains mutation=false with identical control SHA before/after `d81ad57b25fd90aef60f4afa010ccb9a5b61f383700311812dd2b5642cd76926`. Dedicated PHP8.4 mirror-semantics runtime validation PASS: `dataset_live_mirror_semantics_audit.php` returned `ok=true`, `production_mutation=false`, `bybit_submit=false`; LONG exact match=true and wrong-profile blocked=true; SHORT exact match=true and wrong-profile blocked=true; DATASET entry proof preserved=true and legacy absent-proof remains empty=true. Follow-up production LIVE state-machine audit remained mutation=false with identical control SHA before/after `d81ad57b25fd90aef60f4afa010ccb9a5b61f383700311812dd2b5642cd76926`. LIVE mirror semantics blocker is CLOSED.

2026-09-08 final LONG+SHORT readiness aggregation: added read-only/no-submit `tools/dataset_long_short_live_readiness_audit.php`. It executes and requires PASS from the real-data DATASET entry audit, isolated LIVE mirror semantics audit, production read-only MANUAL/AUTO state-machine audit, immutable-library fallback audit, and execution-compatibility audit when present; it also reports current LIVE control SHA. Scope is explicitly bounded: this aggregate does not claim real Bybit submit/fill/latency, tick-level exchange-native PM parity, or statistical fairness of future Champion ranking. PHP8.4 aggregate execution PASS: `combat_dataset_long_short_live_readiness_audit_v1` returned `ok=true`, `production_mutation=false`, `bybit_submit=false`; entry, mirror, AUTO state-machine, immutable library fallback, and execution compatibility all PASS with exit code 0. Current production LIVE control remained readable at SHA `d81ad57b25fd90aef60f4afa010ccb9a5b61f383700311812dd2b5642cd76926`. LONG+SHORT application-level LIVE readiness dry-run is CLOSED/PASS. This is not exchange-level certification: real Bybit order submission/latency/fill behavior and exchange-native tick-level PM parity remain outside the proven scope.

2026-09-08 Champion ranking fairness hardening started. Replaced lifetime raw cumulative-PnL ranking in DATASET `shadow_league_v1.php` with `FAIR_EVIDENCE_V1`: a profile becomes ranking-eligible only after at least 5 resolved trades and 2 evaluated hourly cohorts; eligible profiles rank by `avg_pnl_per_resolved - max_drawdown_pnl / resolved`, then resolved count, then deterministic profile ID. This removes the direct lifetime-age advantage of raw cumulative PnL and prevents a one-hour/tiny-sample newcomer from becoming Champion solely on a lucky raw PnL. SHADOW accumulation itself is unchanged and all profiles remain persistent. Added deterministic `tools/profile_shadow_ranking_fairness_audit.php` with an old high-cumulative-PnL profile, a younger better per-trade/risk profile, and an ineligible tiny-sample profile. Important: this is an engineering fairness policy, not a claim of statistical significance; thresholds remain intentionally minimal and need runtime observation/hysteresis review before exchange-level AUTO LIVE. PHP8.4 lint PASS for `shadow_league_v1.php` and `profile_shadow_ranking_fairness_audit.php`, but the submitted terminal output contained no JSON from the fairness audit even though the shell chain returned to prompt. Root cause of the silent fairness audit was identified before Champion acceptance: ineligible profiles used `ranking_score=-INF`; PHP `json_encode()` cannot encode INF, while the audit did not check the encoder result, so it emitted no JSON and still exited 0. This would also have made a real SHADOW snapshot write fail. Fixed fail-safe: ineligible `ranking_score` is now JSON-safe `null`, comparator branches on `ranking_eligible`, and the audit uses `JSON_THROW_ON_ERROR` so serialization failure can no longer produce a false exit-0 PASS. FAIR_EVIDENCE_V1 deterministic behavioral audit PASS under PHP8.4: `ok=true`, `mutation=false`, leader=`NEW_BETTER_UNIT`; normalized/risk-adjusted score ranks the younger better-unit profile first (1.8) over the older raw-cumulative winner (0.6), while `TOO_YOUNG` remains persistent but `ranking_eligible=false`, `ranking_score=null`, rank 3. JSON serialization is verified and process exits 0. Synthetic fairness-policy validation is CLOSED/PASS. Read-only FAIR_EVIDENCE_V1 application to current accumulated real SHADOW artifacts also PASS (`mutation=false`): LONG leader remains G2 with 17 resolved / 4 hours, score -1.1041; G1 has only 4 resolved and is correctly ineligible despite positive avg PnL; G3 has 1 resolved / 1 hour and is ineligible. SHORT leader remains G2 with 41 resolved / 4 hours, score +0.52644; G1 is eligible but ranks below with score -3.02968; G3 has 9 resolved but only 1 hour and is correctly ineligible. Thus current Champion identity remains G2 on both sides under FAIR_EVIDENCE_V1, while tiny/young profiles cannot displace it. Note that LONG G2's negative score is a warning signal, not a ranking bug: it is currently the only eligible LONG profile, so ranking alone must not be interpreted as absolute LIVE quality. A separate Champion absolute-quality gate/hysteresis is required before AUTO LIVE.

2026-09-08 LIVE AUTO absolute-quality gate implemented fail-close in Combat `DatasetChampionControl` as `LIVE_AUTO_QUALITY_V1`. DEMO semantics are intentionally unchanged: DEMO always follows SHADOW rank #1. LIVE MANUAL is unchanged. LIVE AUTO may publish/switch to the current Champion only when SHADOW `ranking_eligible=true`, resolved>=10, hours_evaluated>=3, and FAIR_EVIDENCE `ranking_score > 0`. Otherwise `applyLiveAuto()` returns `AUTO_HOLD_UNQUALIFIED_CHAMPION` and does not publish a new LIVE revision or toggle LIVE ON/OFF; an already-open LIVE revision therefore remains held/managed. Current real-data implication by the latest read-only ranking: SHORT G2 would qualify (41 resolved, 4h, +0.5264); LONG G2 would fail-close on NON_POSITIVE_SCORE (17 resolved, 4h, -1.1041). Added deterministic no-mutation `tools/dataset_champion_quality_gate_audit.php`. This is the absolute quality gate; switch hysteresis between two qualified Champions remains a separate policy question. PHP8.4 `LIVE_AUTO_QUALITY_V1` audit PASS: qualified positive-score case passes; negative score fails with `NON_POSITIVE_SCORE`; young fails `INSUFFICIENT_HOURS`; small sample fails `INSUFFICIENT_RESOLVED`; ranking-ineligible fails `RANKING_INELIGIBLE`; mutation=false. Existing production read-only LIVE state-machine regression also PASS with unchanged control SHA `d81ad57b25fd90aef60f4afa010ccb9a5b61f383700311812dd2b5642cd76926`. Absolute-quality gate is CLOSED/PASS. Switch hysteresis between two qualified Champions implemented as `LIVE_AUTO_HYSTERESIS_V1`, anchored to the current LIVE publication profile (not prior SHADOW rank). DEMO remains immediate rank-1 follower; MANUAL remains unchanged. For a different target/current pair where both are LIVE-auto-qualified and current still exists in SHADOW, AUTO switch requires target score improvement >= max(0.10 absolute, 15% of |current score|). If current LIVE profile is missing from current SHADOW or no longer passes absolute quality, a qualified target may replace it without hysteresis hold. Same-profile semantic/settings/package drift is not blocked by hysteresis. Failed material-improvement check returns `AUTO_HOLD_HYSTERESIS` with no publication and no LIVE ON/OFF change. Added deterministic no-mutation `tools/dataset_champion_hysteresis_audit.php` covering small delta hold, material delta switch, unqualified current replacement, missing-current replacement, and same-profile allowance. PHP8.4 hysteresis audit PASS: small delta 0.05 vs required 0.1425 holds with `INSUFFICIENT_IMPROVEMENT`; material delta 0.25 switches; unqualified current and missing current allow replacement; same-profile remains allowed. Quality-gate regression PASS and production read-only LIVE state-machine regression PASS; mutation=false and production control SHA unchanged `d81ad57b25fd90aef60f4afa010ccb9a5b61f383700311812dd2b5642cd76926`. LIVE_AUTO_HYSTERESIS_V1 is CLOSED/PASS. Read-only current-state production plan inspection PASS (`mutation=false`, control SHA unchanged `d81ad57b25fd90aef60f4afa010ccb9a5b61f383700311812dd2b5642cd76926`). LONG: current LIVE G1, target G2; G2 fails `LIVE_AUTO_QUALITY_V1` with NON_POSITIVE_SCORE (-1.1041), hysteresis returns TARGET_NOT_QUALIFIED, so AUTO would hold. SHORT: current LIVE G1, target G2; G2 qualifies (+0.52644, 41 resolved, 4h), current G1 score -3.02968 is no longer qualified, so hysteresis explicitly allows replacement (`CURRENT_NO_LONGER_QUALIFIED`). `auto_switch_required=false` in this read-only production plan only because the actual profile selection mode is currently MANUAL; if SHORT is explicitly changed to AUTO, the same plan is intended to request the G1→G2 switch while preserving LIVE ON. LONG would still fail-close in AUTO. No production mode/state was changed by this audit.

2026-09-08 operator explicitly authorized the real SHORT LIVE profile-selection transition MANUAL -> AUTO while keeping `operator_live_enabled=true`. Production control was changed only for `exhaustion_short_120m_v1.profile_selection_mode`; LONG remains MANUAL/OFF and no profile/revision was directly published by this mutation. Control SHA changed from `d81ad57b25fd90aef60f4afa010ccb9a5b61f383700311812dd2b5642cd76926` to `fc96092726096509329bc58c0fbc1d659384adb1d8cf7e978d2e54cabcad0c27`. Expected next Combat engine tick: SHORT qualified Champion G2 is permitted by quality+hysteresis to replace current G1 via a new immutable LIVE revision while preserving LIVE ON. Post-tick production verification COMPLETE/PASS. SHORT is now `profile_selection_mode=AUTO`, `operator_live_enabled=true`, revision 4, profile `SHORT_ROLL24H_G2_20260908_0709`, `published_from=RUNNER_AUTO_CHAMPION`, published at 2026-09-08T13:18:11Z. Thus the natural Combat engine tick performed the qualified G1 -> G2 AUTO publication and preserved LIVE ON exactly as designed. LONG remains MANUAL/OFF on G1; its G2 target remains blocked by NON_POSITIVE_SCORE. Read-only post-switch plan had mutation=false and control SHA stable during the audit (`7c99a029bcbc9195c7068b7027ae7e96dd09bcfbb006664ea617e7df7c70fc95`). Post-switch read-only plan initially reported SHORT `auto_switch_required=true` even though current profile equaled target. Drift decomposition proved profile-definition and settings hashes were already identical; only package version/SHA drifted. Root cause was in the audit tools, not runtime publication: both audits incorrectly read non-existent `storage/live_01/runtime/strategy_packages.json`, then fell back to handler SHA with empty version. Canonical installed package registry is `storage/live_01/control/strategy_packages.json` (also used by `public/strategy-live.php`). Fixed `tools/dataset_live_auto_current_plan_readonly.php` and `tools/dataset_live_auto_state_machine_audit.php` to use the control registry. No production control/runtime mutation. Re-run VERIFIED/PASS. SHORT AUTO production state is idempotent after the G2 publication: current=target `SHORT_ROLL24H_G2_20260908_0709`, quality QUALIFIED, hysteresis SAME_PROFILE, `auto_switch_required=false`, and every drift flag is false (profile ID, definition SHA, settings SHA, package version, package SHA). Revision remains 4, LIVE remains ON. State-machine audit PASS and production control SHA remained unchanged across both read-only audits: `7c99a029bcbc9195c7068b7027ae7e96dd09bcfbb006664ea617e7df7c70fc95`. Repeated-revision concern is CLOSED; the earlier positive drift was solely an audit registry-path bug.

Combat MANUAL profile inventory expanded to satisfy the requirement that the operator can select any execution-compatible DATASET library profile, not only profiles present in current `shadow_league/latest.json`. `DatasetShadowLeagueConsumer::compatibleLibraryDefinitions()` enumerates immutable side-specific library artifacts and reuses the existing fail-closed by-ID compatibility resolver; invalid/incompatible artifacts are omitted. `public/strategy-live-control.php` now merges compatible immutable library definitions into `$liveProfileOptions` after current SHADOW rows, without overriding current-row definitions. Added no-mutation `tools/dataset_manual_profile_inventory_readonly.php` reporting latest-compatible, library-compatible, and library-only profile IDs per side. Existing MANUAL hot-swap guard remains: publishing a different profile while LIVE ON is forbidden. PHP8.4/runtime MANUAL inventory verification CLOSED/PASS (`mutation=false`). LONG: latest-compatible=3, library-compatible=3 (G1/G2/G3), library-only=0. SHORT: latest-compatible=3, library-compatible=3 (G1/G2/G3), library-only=0. Thus current SHADOW snapshot already contains the entire current immutable library on both sides, while the UI is now future-proof for later library-only historical profiles: such compatible artifacts will remain operator-selectable after they disappear from a future `latest.json`. No production state mutation. MANUAL inventory requirement is CLOSED/PASS.

SHADOW evidence identity hardening implemented. `DatasetCausalFeatureBuilderV1` now emits deterministic `signal_id = SHA256(base_signal_contract|SIDE|SYMBOL|ts)` for every causal base-signal row. `DatasetProfileShadowLeagueV1` deduplicates each matured cohort by signal_id before collection, derives deterministic `cohort_id = SHA256(contract|side|from|to|ordered unique signal IDs)`, exposes cohort_id/unique_signal_ids in latest snapshot, and persists a rolling 168-cohort ID ledger per profile. `merge()` fail-closes duplicate cohort accumulation: if the same cohort_id is already present for that profile, cumulative evidence/hours/PnL are not incremented again. Legacy rows without signal_id receive deterministic LEGACY side/symbol/ts identity inside the league. Added no-mutation deterministic `tools/profile_shadow_signal_identity_audit.php`. This protects repeated signal rows and replayed cohort accumulation; it does not yet solve late-profile common-age fairness or historical backfill. PHP8.4 deterministic signal/cohort identity audit CLOSED/PASS (`mutation=false`): contract `COMBAT_MATCH_CONFIRMED_5M_PARITY_V1`, same logical signal stable across side/symbol case normalization, different timestamp distinct, 3 synthetic rows deduplicated to 2 unique signal IDs, deterministic cohort ID stable. First runtime inspection after the identity patch did NOT yet observe a post-patch matured cohort: both LONG and SHORT `latest.json` are still the pre-patch snapshots generated at ts 1788872477/1788872478 for cohort 1788865200..1788868800. As expected for legacy snapshots, they have no `cohort_id`, no `unique_signal_ids`, and profile `cohort_ledger_count=0`. Existing cumulative evidence is intact (LONG G2 4h/17/7.6026263, G1 4h/4/5.6528713, G3 1h/1/5; SHORT G2 4h/41/21.5840053, G1 4h/44/-66.65304965, G3 1h/9/0.6456567). Therefore runtime migration is neither PASS nor FAIL yet; wait for the next normal SHADOW cron matured cohort and re-inspect. Do not manually replay the already-accounted legacy cohort, because its old profile artifacts have no cohort ledger entry and a forced replay could double-count it. Added `tools/profile_shadow_identity_runtime_readonly.php`: a no-mutation runtime migration gate that returns PASS only when both sides expose cohort_id + unique_signal_ids, base_signals equals unique_signal_ids after dedup, and every current profile has a non-empty evaluated-cohort ledger plus last_cohort.cohort_id. Until the next natural matured cron cohort it is expected to exit non-zero/PENDING on the legacy snapshot. Cron diagnosis confirms the scheduler is healthy: `profile_shadow_league` is enabled, status COMPLETE, last_start=last_finish=1788872537, last_error=null, done=2/2; subsequent cron-center passes report `NOT_DUE`. Parser2/Passport dependencies are also completing. Therefore the unchanged identity snapshot is scheduler cadence, not a stuck lock or task failure. With interval_sec=3600, next due is approximately 1788876137 (14:02:17 UTC); inspect only after that due execution completes. RUNTIME PASS after the natural due execution: LONG generated_ts=1788876192, cohort 1788868800..1788872400, cohort_id=43bdb10714009f5f016ad3c6f8b0ba255b74d34480491042f6981d4797111b97, base_signals=unique_signal_ids=13; SHORT generated_ts=1788876193, same interval, cohort_id=9556b23a38736a6de831db0d4b5290d5757f088c08f0d718d3078d5c5497ad29, base_signals=unique_signal_ids=23. Both sides report identity_migrated=true, dedup_invariant=true, profile_ledger_ok=true; every current G1/G2/G3 artifact has cohort_ledger_count=1 and last_cohort_id equal to the side cohort. Cumulative evidence continued (no reset): LONG G1 h5/r9/pnl18.8266299383, G3 h2/r6/pnl12.3188519812, G2 h5/r28/pnl-39.2534143421; SHORT G3 h2/r15/pnl25.7476779663, G2 h5/r47/pnl46.6860265743, G1 h5/r49/pnl-48.9363762919. Deterministic signal/cohort identity runtime migration is CLOSED/PASS. Added Combat read-only chain inspector `tools/dataset_champion_chain_current_readonly.php` to inspect, without applying anything, the post-cohort FAIR_EVIDENCE ranking, selected Champion, DEMO target/settings, and LIVE MANUAL/AUTO quality+hysteresis+drift decision for both sides; it hashes LIVE control before/after and fails on mutation. Runtime chain PASS/no mutation after the identity cohort: LONG Champion changed to G1 (score 2.0918477709, resolved 9, hours 5), G3 rank2 (2.0531419969, r6/h2), G2 rank3 (-4.0172148231, r28/h5, maxDD 73.2286007044). LONG DEMO is already profile G1 but reports settings_sync_required=true; LONG LIVE remains MANUAL/OFF rev1 G1 and LIVE AUTO quality would reject G1 for resolved<10. SHORT Champion changed to G3 (score 1.7165118644, r15/h2), G2 rank2 (0.9933197143, r47/h5), G1 rank3 (-2.3589678764). SHORT DEMO is already profile G3 but reports settings_sync_required=true. SHORT LIVE remains AUTO/ON rev4 on G2; target G3 is rejected by LIVE_AUTO_QUALITY_V1 solely for hours<3, hysteresis TARGET_NOT_QUALIFIED, auto_switch_required=false, so LIVE correctly holds G2 despite SHADOW/DEMO Champion G3. Control SHA remained 7c99a029bcbc9195c7068b7027ae7e96dd09bcfbb006664ea617e7df7c70fc95. Added no-mutation `tools/dataset_demo_settings_drift_readonly.php` to decompose DEMO `settings_sync_required=true` into exact key/value/type differences between StrategyRuntimeControl current settings and DatasetChampionControl expected Champion settings, while hashing runtime/live control files before/after. This distinguishes a normal pending engine-tick sync from persistent normalization/type drift. Runtime audit found the latter: both LONG and SHORT had exactly five false-positive differences where JSON-loaded current values were ints and Champion risk mapping produced numerically identical floats (`hard_stop_roi_pct`, `pm_arm_roi_pct`, `pm_trail_gap_roi_pct`, `profit_floor_roi_pct`, `profit_lock_roi_pct`); no value difference and no mutation. Fixed `DatasetChampionControl::plan()` to use semantic `settingsDrift()` comparison: int/float pairs are equal when their numeric float values are identical, while key-set and all other strict value differences still trigger sync. This prevents endless DEMO settings rewrites/timestamp churn caused only by JSON number representation. No LIVE state was mutated by the patch/audit. Post-fix runtime verification PASS: PHP lint clean; both LONG and SHORT now report `switch_required=false`, `profile_switch_required=false`, `settings_sync_required=false`. Diagnostic raw diff still lists the five int-vs-float representation pairs by design, proving semantic comparison—not data rewriting—closed the false drift. Champion chain remains stable: LONG Champion/DEMO G1, LIVE MANUAL/OFF rev1; SHORT Champion/DEMO G3, LIVE AUTO/ON rev4 still G2 because G3 is `INSUFFICIENT_HOURS` (15 resolved, 2 hours), hysteresis `TARGET_NOT_QUALIFIED`, `auto_switch_required=false`. LIVE control SHA before/after remained 7c99a029bcbc9195c7068b7027ae7e96dd09bcfbb006664ea617e7df7c70fc95. DEMO numeric type-drift issue is CLOSED/PASS. Added `combatsolo.info/tools/dataset_live_auto_transition_watch_readonly.php` for the next natural SHADOW cohort. It reports, per side, LIVE mode/ON/revision/current profile, target Champion, LIVE_AUTO_QUALITY_V1, LIVE_AUTO_HYSTERESIS_V1, auto_switch_required, a single derived decision (`MANUAL_NO_CHANGE`, `AUTO_HOLD_UNQUALIFIED_CHAMPION`, `AUTO_HOLD_HYSTERESIS`, `AUTO_SWITCH_REQUIRED`, or `ALREADY_CHAMPION`), and drift; LIVE control SHA is checked before/after and any mutation fails the audit. Intended use is observation only around the next natural league cohort; do not force replay or LIVE publication. First watcher run was still on the same league snapshot (LONG generated_ts=1788876192, SHORT=1788876193), so it did NOT yet observe the next SHADOW cohort. Read-only result: LONG `MANUAL_NO_CHANGE`; SHORT `AUTO_HOLD_UNQUALIFIED_CHAMPION`, current LIVE G2 rev4 AUTO/ON, target G3 still r15/h2/score1.7165118644 with `INSUFFICIENT_HOURS`; no switch and control SHA unchanged. Recheck only after DATASET `profile_shadow_league` naturally advances `league_generated_ts`; do not infer a failed cron or force replay from this unchanged snapshot. A subsequent check at ~19:09 +03 still showed the exact same generated_ts/cohort IDs and evidence (LONG 1788876192, SHORT 1788876193; SHORT G3 r15/h2), with Combat still `AUTO_HOLD_UNQUALIFIED_CHAMPION` and LIVE control unchanged. Because enough wall time has now passed to justify diagnosing scheduling rather than repeatedly polling league output, added read-only `datasetis.info/tools/cron_shadow_status_readonly.php`. It calls CronCenter `status()` only and reports enabled/status/interval/last start/finish/next run/due/heartbeat/error for `profile_shadow_league`, while hashing cron state before/after. This does not call `tick()` and cannot intentionally run SHADOW. Cron status at 2026-09-08T16:11:43Z confirmed scheduler health: ACTIVE/execution enabled, `profile_shadow_league` enabled COMPLETE, last start=finish 1788883637, no error, next run 1788887237, due_now=false. Therefore the external cron is arriving and the SHADOW task did run at 16:07:17Z; unchanged league snapshot means that invocation returned without evaluating a new mature hour (most likely `ALREADY_EVALUATED` for the same computed hour), not a scheduler failure. Next scheduler eligibility is ~17:07:17Z. Also corrected the read-only audit's cron-state hash path from `storage/data/control/cron_center.json` to canonical `storage/control/cron_center.json`; the previous null/null hashes were an audit-path mistake, not missing cron state. Natural runtime transition subsequently observed and is material: new league snapshot generated_ts LONG=1788883571 / SHORT=1788883572, cohort ledgers advanced from 1 to 3 (two additional unique cohorts accumulated). LONG G1 remains Champion and is now LIVE_AUTO quality-qualified (r23/h7/score 6.0664), while LONG LIVE remains intentionally MANUAL/OFF rev1. SHORT evidence reversed sharply: G2 rank1 score -0.66984 (r67/h7), G1 rank2 -2.26138, G3 rank3 -2.46355 (r36/h4). DEMO correctly auto-returned from G3 to Champion G2. Critically, before this read-only observation SHORT LIVE had naturally advanced from rev4/G2 to rev5/G3 under AUTO+ON, proving the real qualified AUTO transition occurred when G3 became eligible/qualified on an intermediate cohort. By the latest snapshot G3 had deteriorated and Champion returned to G2, but G2 itself now fails LIVE_AUTO_QUALITY_V1 (`NON_POSITIVE_SCORE`), so policy correctly holds existing LIVE G3 (`AUTO_HOLD_UNQUALIFIED_CHAMPION`) instead of switching to an unqualified rank-1 profile. LIVE control SHA was stable during the read-only audit at 9a9aecbad122749dd3702c9588618ab10a9c6dafc7a6dc47324ed308b6bd3a7e. This closes the previously unobserved real `quality -> hysteresis -> LIVE AUTO publication` transition path; it also demonstrates the intended distinction between SHADOW Champion/DEMO and LIVE AUTO eligibility. Started LIVE AUTO degradation hardening without mutating/enforcing against real LIVE. `DatasetChampionControl` now exposes `LIVE_AUTO_DEGRADATION_V1` in plan/audit: only AUTO+ON with a current profile present in SHADOW is evaluated; the current LIVE profile is scored by the same `LIVE_AUTO_QUALITY_V1`; if it is no longer qualified, `pause_new_entries_required=true`. Enforcement is explicitly `AUDIT_ONLY` for now, so engine tick behavior and LIVE control are unchanged. Added `combatsolo.info/tools/dataset_live_auto_degradation_readonly.php` with before/after LIVE-control SHA guard. This separates policy validation from the later explicit decision to make AUTO fail-close operational. First real read-only degradation audit exposed and immediately fixed a policy-output bug: SHORT G3 correctly evaluated `live_auto_qualified=false / NON_POSITIVE_SCORE` and reason `CURRENT_NO_LONGER_QUALIFIED`, but `pause_new_entries_required` incorrectly remained false because PHP array union (`$base + [...]`) preserved the base false value. The final branch now uses `array_merge`, allowing the derived pause=true value to override the default. No LIVE state was mutated; observed control SHA remained 9a9aecbad122749dd3702c9588618ab10a9c6dafc7a6dc47324ed308b6bd3a7e. Added isolated `dataset_live_auto_degradation_policy_audit.php` covering qualified AUTO, degraded AUTO, MANUAL, AUTO/OFF, and missing-current cases so this boolean/reason invariant cannot regress silently. Enforcement remains AUDIT_ONLY pending post-fix runtime + synthetic PASS. Post-fix validation is now CLOSED/PASS: isolated degradation policy audit passed all five cases (AUTO+ON qualified=>no pause; AUTO+ON degraded=>pause; MANUAL=>no pause; AUTO+OFF=>no pause; current missing=>no automatic pause), and real read-only SHORT rev5/G3 returned `CURRENT_NO_LONGER_QUALIFIED`, quality `NON_POSITIVE_SCORE`, r36/h4/score -2.4635458303, with `pause_new_entries_required=true`. LONG MANUAL/OFF correctly returned NOT_AUTO/no pause. LIVE control SHA remained unchanged at 9a9aecbad122749dd3702c9588618ab10a9c6dafc7a6dc47324ed308b6bd3a7e. Therefore degradation detection itself is CLOSED/PASS; only operational enforcement remains intentionally absent and is outside the frozen required completion list, so no further degradation enforcement will be added. Closed the remaining required audit-tool path defect: `combatsolo.info/tools/dataset_demo_settings_drift_readonly.php` now hashes canonical `storage/live_01/control/strategy_runtime_control.json` instead of nonexistent `runtime_control.json`, alongside `live_promotion_control.json`. This is read-only guard hardening only; no production/runtime state mutation. Runtime verification CLOSED/PASS: both canonical guard hashes are non-null and unchanged before/after (`strategy_runtime_control.json` b81a11bd4a1843a32765961b980bb2cfe5d9a364a8767678eae6247571847502; `live_promotion_control.json` 9a9aecbad122749dd3702c9588618ab10a9c6dafc7a6dc47324ed308b6bd3a7e), mutation=false, and both LONG/SHORT report switch/profile/settings sync false. Raw diagnostic still lists five int-vs-float representation differences by design; semantic production comparator correctly treats their equal numeric values as no drift. Required DEMO settings-drift/hash-guard item is therefore CLOSED/PASS. Combat strategy overview equity observability improved for both LONG and SHORT without changing trading logic/state: `public/strategy.php` now maps equity X coordinates proportionally to persisted `closed_ts`/runtime equity timestamps instead of equal spacing per trade, and renders a five-tick real-time X axis below both initial and live-refresh equity views. The live JS renderer uses the same timestamp-proportional mapping and refreshes localized time labels. This makes inactivity gaps, drawdown/recovery duration, and visually apparent profile decay speed observable while preserving the existing cumulative PnL Y-axis and all execution semantics. Added explicit one-off DATASET qualification tool `tools/profile_runner_long_fresh24h_once.php` for the requested fresh LONG experiment. It forces an immediately preceding 24h training window only and requested execution/risk contract 10 USDT initial margin, 5x leverage, hard stop -10% ROI, profit floor/lock +5% after +10% arm, trailing gap 5% ROI. Safety gates require qualification dry-run with production runner/promotion/Combat publication disabled; output is SHADOW-candidate build only and does not mutate Combat/LIVE. The existing bounded 80-symbol causal builder/resume mechanism is preserved, so completion may require subsequent qualification resume batches before a formula is finalized. First operator invocation hit `LONG_CHALLENGER_NOT_DUE` because the generic rolling engine inherited the normal 60-minute rebuild cadence. The explicit one-off tool was corrected to override cadence to 1 minute for that invocation only; persistent runner configuration/cadence is unchanged. A collision within the same minute fails closed with `LONG_CHALLENGER_ONE_OFF_COLLISION_RETRY_AFTER_60S`. G6 completion exposed a resume-contract bug: the manual qualification resume reloads the module config, so the formula was initially finalized with the default stop grid and selected -30 despite G6 persisting `risk_request.hard_stop_grid_roi_pct=[-10]`. `hourly_pipeline_v1.php` now reapplies a challenger's immutable `risk_request` before formula search on completion. Added one-purpose repair tool `tools/profile_runner_rebuild_existing_long_g6_requested_risk.php` to recompute the already-complete G6 formula from its immutable chunks with the persisted -10 grid; it creates no generation, does not publish SHADOW/Combat, and does not mutate LIVE. Corrected G6 result: 123 causal rows, 19 allowed (15.45% traffic), 15W/4L, training WR 78.95%, PnL proxy +51.55, conditions `range_5m_pct<=1.0919017288444068` and `volume_5m>=118798`, risk -10/+5/+10/trailing-gap 5. Added guarded one-purpose registration tool `tools/profile_runner_register_existing_long_g6_shadow.php`: it verifies the corrected risk invariant and immutably records G6 into the DATASET LONG library as `SHADOW_ACTIVE`; no historical backfill, Champion publication, Combat publication, or LIVE mutation. Evidence begins on subsequent persistent SHADOW mature cohorts. Profile Generator control-plane v1 started in Combat strategy UI: a sticky right-column `Profile Generator` form now captures side, free label, immutable 1..720h training window, leverage, budget, hard stop, profit floor, trailing arm/gap, and post-build policy `COMPETE` or `ACTIVATE_DEMO`. Combat writes validated queued request documents under its own control storage; requests explicitly prohibit LIVE mutation/direct LIVE publish. DATASET adds `generation_request_inbox_v1.php` as the receiving contract validator; DATASET remains sole builder/data/library/SHADOW owner. AUTO formula features are the existing causal set (ret 1h/24h, range 5m, volume, turnover). Cross-domain generator transport is now wired through authenticated Combat live-engine credentials to DATASET `/api/v1/live_engine/profile_generation.php`; DATASET authenticates the approved engine, validates the immutable request and stores an idempotent inbox document. Added `generation_worker_v1.php` plus guarded `tools/profile_generation_worker_once.php`: it consumes one queued/building request per invocation, freezes the requested arbitrary 1..720h window, applies requested execution/risk, reuses the resumable 80-symbol causal pipeline, finalizes the formula, and immutably records a completed profile into DATASET Library/SHADOW. `COMPETE` ends there. `ACTIVATE_DEMO` completion is now reconciled back by Combat through authenticated read-only DATASET status endpoint `profile_generation_status.php`. Combat then resolves the exact immutable Library profile through `DatasetShadowLeagueConsumer::executionDefinitionById()` and `DatasetGeneratedDemoActivation` updates only that side's DEMO runtime profile/risk settings. It does not call Champion selection, so an explicit generated ORIGINAL can become DEMO even when it is not rank #1; the profile simultaneously remains in persistent SHADOW. `COMPETE` never performs this activation. LIVE promotion/control is not invoked and remains untouched. Generator UI/control was moved toward a dedicated Combat `/profile-generator` workspace: it is linked from the left menu, shows generation parameters/history, current batch progress, and drives DATASET generation batches only while the page remains open (browser → authenticated Combat control → authenticated DATASET tick). Generation therefore does not require a cron task; closing the page pauses batch advancement and reopening it resumes the same persisted DATASET request. First page-driven E2E exposed a browser `Failed to fetch` while the request remained DATASET `QUEUED`; the Combat→DATASET per-batch bridge had a 25-second cURL ceiling, too short for one causal 80-symbol batch. A second page-driven attempt still returned browser `Failed to fetch` with DATASET remaining `QUEUED`, showing that merely extending the synchronous bridge timeout is not sufficient under the web/proxy request ceiling. Generator-specific causal batches were first reduced from 80 to 20 symbols, but a third page-driven attempt still failed before the first persisted cursor advance and remained DATASET `QUEUED`. To stay safely below the observed web/proxy synchronous request ceiling without introducing cron/background execution, generator-only batches are now 5 symbols per tick with an 18-second bridge timeout/connect 2 seconds. The trade-off is more browser ticks, while the immutable request window, persisted resumability and final formula calculation are unchanged. A subsequent 5-symbol attempt still produced browser-level `Failed to fetch`, so batch duration is no longer treated as the primary fault. The generator browser transport was hardened to POST to the exact current pathname (preserving the canonical trailing slash and avoiding a POST redirect), explicitly include same-origin credentials, disable cache, send an XHR marker, and serialize the URL-encoded body as a string. The exact-path browser POST fix subsequently completed `GEN_LONG_20260908_180854_BA3A3C` end-to-end, confirming the failure was the Combat browser route/redirect transport rather than DATASET batch duration. Generator batch size is therefore restored to 80 symbols. Universal Profile Generator v1 now also supports AUTO or MANUAL formula mode: MANUAL accepts 1..5 unique conditions over the frozen causal allowlist (`ret_1h_pct`, `ret_24h_pct`, `range_5m_pct`, `volume_5m`, `turnover_5m`) using only `<=`/`>=`; DATASET validates the contract and evaluates the exact supplied conjunction instead of running beam search. Empty operator label is accepted and normalized to `GEN`; labels remain metadata only. A critical UI replay bug was then found in the page-driven flow: the initial Generate action rendered the generator page directly as the response to the form POST, so the JavaScript `location.reload()` after each COMPLETED build replayed that POST and created another generation request/profile indefinitely. The Generate action now uses strict POST/Redirect/GET (HTTP 303 to `/profile-generator/?accepted=<request_id>`); all subsequent browser reloads are GET and therefore cannot create another profile. Existing accidental duplicate profiles are left untouched pending explicit cleanup approval. Profile Generator history UI now exposes the generated profile's actual training identity instead of only opaque IDs: formula mode and exact conditions, causal/base rows, allowed rows and traffic %, wins/losses, training WR, pnl proxy, and AUTO candidate count are rendered from the DATASET challenger evidence when available. Risk/window/mode columns remain visible alongside these metrics. Generator lifecycle control now includes guarded deletion: the UI can delete one generated profile or all generated profiles. Combat blocks deletion when a generated profile is referenced by current DEMO/LIVE control; DATASET independently blocks the current SHADOW Champion and active builds, verifies generator ownership, and removes only generator-owned request/library/challenger/build-chunk/evidence artifacts plus stale profile references from profile-runner state. Bulk deletion iterates generated requests and leaves protected profiles intact; ordinary qualification/system profiles are outside the deletion set. Resumability is unchanged, so a 520-symbol build advances in more small ticks rather than fewer long ticks. This batch-size change is generator-only and does not change the normal hourly qualification batch contract. DATASET added owner-checked `profile_generation_tick.php`, and the worker can now tick a specific request ID instead of consuming an unrelated queued request. Combat status reconciliation now continues through non-terminal `BUILDING` states instead of stopping after the first remote poll. LIVE remains untouched. Generator transport auth was corrected after the first UI E2E attempt returned `LIVE_ENGINE_TOKEN_INVALID`: DATASET `ds_live_engine_token()` expects the established `X-Live-Engine-Token` header, while the new generator client initially sent Bearer only. Generator submit and status reconciliation now send both headers exactly like the existing `DatasetApprovalClient` transport; no credential rotation/re-registration is required. The first failed local request `GEN_LONG_20260908_174406_10E0D0` remains only local `QUEUED` because DATASET rejected it before inbox acceptance. LONG remains MANUAL/OFF with G2 blocked by NON_POSITIVE_SCORE. Next matured-cohort runtime verification also remains pending. Next required gate is actual DATASET rolling-profile runtime-entry parity/bar-history readiness before LIVE AUTO wiring.

Champion DEMO runtime retry exposed a second idempotency defect (2026-09-08): SHORT profile id had already been persisted as G2 during the first partial apply, so `applyDemo()` treated it as `ALREADY_CHAMPION` and did not repair the stale invalid trigger=10 settings; the runtime therefore failed again with `EXHAUSTION_SHORT_SETTINGS_INVALID`. Fixed `DatasetChampionControl::plan()` so DEMO `switch_required` means profile mismatch OR target-settings drift, with explicit `profile_switch_required` and `settings_sync_required`. Thus a partially applied Champion state self-repairs on the next tick instead of being considered converged. Retry pending; LIVE untouched.

Champion DEMO runtime validation found and fixed a Combat adapter invariant bug (2026-09-08): first deterministic `bin/cron.php` attempt failed closed with `EXHAUSTION_SHORT_SETTINGS_INVALID`. SHORT runtime control had G2 floor=5, trailing arm=10, but retained legacy `profit_protect_trigger_roi_pct=10`; SHORT handler requires `floor < trigger < arm`, so Champion mapping made trigger==arm. `DatasetChampionControl::settings()` now derives the legacy Combat trigger deterministically as the midpoint between DATASET floor and trailing arm (G2: 7.5), without changing DATASET PM contract values (floor=5, arm=10, gap=3). This is a compatibility adapter field only. Retry pending; LIVE untouched.

Runtime follow-up: manually invoking `bin/master-cron.php` returned success but left `last_tick.json` unchanged at `2026-09-08T12:12:48+01:00` with `champion_auto=null`. This is consistent with master-cron's nonblocking `master_cron.lock` path: when another master-cron owns the lock it exits 0 without running `EngineTickRunner`. Therefore use the single-cycle `bin/cron.php` for the next deterministic verification; it has no master-cron wrapper lock, while `EngineTickRunner` itself still has its own nonblocking `engine_tick.lock`. Runtime diagnosis: the inspected `storage/live_01/runtime/last_tick.json` was generated at 2026-09-08T12:12:48+01:00, while the patched `engine/cron/engine_tick.php` mtime is later (1788865951 vs tick ts_unix 1788865968 is only 17s difference but the persisted tick contains no `champion_auto` field), so that persisted row did not execute the newly instrumented Champion block as expected; require a fresh post-patch master-cron tick and inspect `champion_auto` explicitly before diagnosing LONG persistence. First post-wiring read-only runtime plan audit: SHORT DEMO successfully reached current Champion `SHORT_ROLL24H_G2_20260908_0709` (`switch_required=false`), while LONG DEMO remained `COMBAT_LONG_ORIGINAL_G0` with target `LONG_ROLL24H_G2_20260908_0709` (`switch_required=true`), so full LONG+SHORT runtime activation is NOT yet PASS. LIVE remained unchanged and correctly isolated: LONG MANUAL/OFF/G1, SHORT MANUAL/ON/G1, both `live_auto_switch_required=false`. Next action is diagnose why LONG applyDemo did not execute/persist; do not mutate LIVE. When Combat DEMO execution is enabled and the corresponding package is installed, `EngineTickRunner` invokes shared `DatasetChampionControl::applyDemo()` for SHORT and LONG immediately before each DEMO runner. Thus rank #1 DATASET SHADOW Champion becomes the settings/profile snapshot used by subsequent new DEMO scans; existing open owners continue using their stored `settings_snapshot`. The path does not call `applyLiveAuto()`, does not publish LIVE revisions, and does not toggle LIVE ON/OFF. Idempotency is provided by `switch_required`/`ALREADY_CHAMPION`. This wiring is an actual DEMO control mutation on the next normal engine tick; real LIVE remains untouched.

DATASET real G2 structured-rule parity PASS (2026-09-08): operator reran `tools/dataset_g2_real_parity_audit.php` after audit-window fixes; schema `combat_dataset_g2_real_parity_audit_v1`, `ok=true`, `mutation=false`. Deterministic first-80 Parser2 cohort: LONG current rank #1 `LONG_ROLL24H_G2_20260908_0709` had 6 base signals / 6 complete evaluations / 0 mismatches; SHORT current rank #1 `SHORT_ROLL24H_G2_20260908_0709` had 88 base signals / 88 complete evaluations / 0 mismatches. This closes the real-data structured-rule decision parity gate for current G2 LONG+SHORT. It does not by itself validate exchange/tick execution or mutate DEMO/LIVE. Next gate is Champion→DEMO AUTO control activation with LIVE remaining untouched.

DATASET real-data parity audit third run exposed snapshot-schema mismatch in the audit (2026-09-08): 80 Parser2 symbols were loaded but zero signals because the audit read nonexistent top-level `hour_ts`; the actual `dataset_shadow_league_snapshot_v1` carries the evaluated interval under `cohort.from_ts/to_ts` (current LONG cohort explicitly reports 5 base signals for 1788854400..1788858000). Audit corrected to anchor its trailing-24h evaluation at `cohort.to_ts`. This is audit-only; runtime/DEMO/LIVE state remains unchanged. Re-run required and must be non-vacuous.

DATASET real-data parity audit second run also correctly failed non-vacuity (2026-09-08): Parser2 path fix exposed 80 symbols, but base/evaluations remained zero. Root cause was another audit-only window bug: the script loaded only the 24h evaluation interval and then evaluated only timestamps `>= from+86400`, effectively testing only the terminal timestamp; it also lacked the preceding 24h lookback required by `ret_24h_pct`. Corrected audit now loads `[evaluation_from-24h, evaluation_to]` while evaluating the full `[evaluation_from, evaluation_to]`. No runtime/DEMO/LIVE state changed; re-run required.

DATASET real-data parity audit first run correctly failed non-vacuity (2026-09-08): both sides reported `symbols=0`, `base_signals=0`, `complete_evaluations=0`, `mismatch=0`, so this is NOT a parity failure and cannot be counted PASS. Root cause was an audit-only wrong Parser2 path (`datasetis.info/storage/parser2_market_cache/storage`); DATASET local source layout is `datasetis.info/storage/data/parser2_market_cache/storage` (`DatasetConfig::localDataRoot = storage/data`). Audit path corrected; no runtime/DEMO/LIVE code or state changed. Re-run required.

DATASET rolling-profile entry wiring PHP 8.4 lint reported clean by operator (2026-09-08). Added read-only real-data audit `tools/dataset_g2_real_parity_audit.php`: it resolves current rank #1 independently for LONG/SHORT, scans a deterministic first-80 Parser2 symbol cohort over the latest league-aligned 24h, requires the frozen DATASET base signal, computes Combat causal features from real confirmed 5m bars, and checks structured-condition decisions with zero allowed mismatch. `mutation=false`; it performs no exchange/API/order/control writes. Pending runtime audit result.

DATASET rolling-profile entry wiring implemented, pending lint/parity (2026-09-08): both active Combat DEMO runners now recognize `LONG_ROLL24H_G*` / `SHORT_ROLL24H_G*` as DATASET execution profiles, resolve their immutable conditions through `DatasetShadowLeagueConsumer::executionDefinitionById()`, and apply `DatasetStructuredRuleEvaluator` immediately after the frozen base-signal matcher and before order-entry checks. Missing profile, incomplete causal data, failed condition, unknown feature/operator all fail closed and emit explicit `ENTRY_SKIPPED_DATASET_PROFILE_*` events. SHORT package normalization was extended to preserve valid `SHORT_ROLL24H_G*` ids instead of silently falling back to ORIGINAL. Legacy/non-DATASET profile paths remain unchanged. No Champion->DEMO mutation or LIVE control mutation was performed by this wiring. Pending PHP 8.4 lint and real G2 parity audit before enabling automatic DEMO promotion.

DATASET structured-rule evaluator synthetic audit PASS (2026-09-08): PHP 8.4 lint passed for both evaluator and audit; `combat_dataset_structured_rule_evaluator_audit_v1` returned `ok=true`, `mutation=false`. Exact synthetic outputs confirmed ret_1h=-5.263157894736848%, ret_24h=-10%, range_5m=3.3333333333333335%, allow/block condition behavior, and fail-closed rejection of unknown features. Contract remains `DATASET_CAUSAL_FEATURES_V1`. No DEMO/LIVE mutation occurred. Next gate: wire the evaluator into both active entry runners and validate against real DATASET G2 conditions.

DATASET structured-rule execution contract implementation started (2026-09-08): added Combat `engine/strategies/dataset_structured_rule_evaluator.php`. It deterministically computes the exact causal feature formulas used by DATASET `DatasetCausalFeatureBuilderV1` at the confirmed 5m signal timestamp (`ret_1h_pct`, `ret_24h_pct`, `range_5m_pct`, `volume_5m`, `turnover_5m`) and applies an allowlisted condition/operator set fail-closed. Added read-only synthetic contract audit `tools/dataset_structured_rule_evaluator_audit.php`; pending PHP 8.4 lint/runtime PASS. The evaluator is not yet wired into LONG/SHORT runners, so no DEMO/LIVE behavior changed.

Combat runtime Champion blocker found (2026-09-08): ownership isolation is confirmed in both DEMO runners: open positions persist `settings_snapshot`, and management reads the owner snapshot before applying PM/risk, so changing global runtime settings can safely affect only subsequent scans/entries. However DATASET rolling profiles are NOT yet executable by the current entry runners. SHORT `normalizeSettings()` rejects arbitrary `SHORT_ROLL24H_*` ids back to `ORIGINAL`, and the runner only resolves profiles from manifest `entry.profiles`; LONG accepts arbitrary profile ids in settings but its runner has no DATASET `structured_rules` consumer. Searches of both active package handlers found no `structured_rules` evaluator. Therefore automatic Champion->DEMO mutation remains blocked until a shared deterministic DATASET structured-rule evaluator is wired into both LONG/SHORT entry paths and parity-audited. This is a hard LIVE-readiness blocker, not a UI issue.

DEMO switch-path audit finding (2026-09-08): the historical SHORT UI creates `short_profile_switch_control.json` with a next-runner-lock/cooldown policy, but no active runtime consumer of that control artifact was found in the current Combat tree; only the UI writer and historical backup activation event exist. Therefore this legacy switch file must not be treated as an effective safety mechanism for DATASET Champion promotion. Current runtime trades already persist entry profile/settings ownership, so the target Champion control will use atomic runtime settings replacement for new scans/entries while relying on immutable per-position ownership for already-open positions. Before enabling automatic mutation, this ownership invariant and actual entry evaluator must be audited explicitly for both LONG and SHORT.

Shared DATASET Champion control adapter lint/plan audit PASS (2026-09-08): PHP 8.4 lint passed for `dataset_champion_control.php` and `dataset_champion_plan_audit.php`; read-only audit returned `ok=true`, `mutation=false`. Current Champion targets are LONG G2 (`LONG_ROLL24H_G2_20260908_0709`) and SHORT G2 (`SHORT_ROLL24H_G2_20260908_0709`). Both DEMO runtimes require a switch (LONG from `COMBAT_LONG_ORIGINAL_G0`, SHORT from `ORIGINAL`). Both LIVE controls are MANUAL, therefore no AUTO transition is requested: LONG LIVE is OFF on G1; SHORT LIVE is ON on G1. This confirms DEMO auto-follow is the missing active transition while MANUAL LIVE remains isolated. No state mutation occurred.

Shared DATASET Champion control adapter started (2026-09-08): Combat now has `engine/strategies/dataset_champion_control.php`, a single planner/adapter for both LONG and SHORT. It reads DATASET SHADOW rank #1 through the existing read-only consumer, builds one execution/risk settings target, exposes DEMO-always-follow and LIVE-AUTO-follow decisions, and has separate `applyDemo()` / `applyLiveAuto()` mutation methods. No mutation method has been invoked during implementation. `tools/dataset_champion_plan_audit.php` is read-only (`mutation=false`) and is intended to verify current Champion/current DEMO/current LIVE mode and required transitions before wiring cron. Pending PHP 8.4 lint and audit runtime. Automatic cron wiring remains intentionally absent until those checks pass and runtime decision parity is hardened.

LIVE-readiness completion plan frozen (2026-09-08): target control semantics are now explicit: (1) every DATASET library profile remains in persistent SHADOW; (2) current qualified SHADOW Champion automatically becomes Combat DEMO ACTIVE, with old DEMO returning to SHADOW while existing positions retain their entry snapshot; (3) LIVE has two profile-selection modes, MANUAL and AUTO; MANUAL changes only by operator action, AUTO follows the same Champion as DEMO; (4) LIVE ON/OFF remains a separate execution switch in both modes; (5) Champion consumption must use one shared DATASET->Combat adapter for DEMO and LIVE; (6) profile changes create immutable revisions/snapshots and must not rewrite open-position entry risk/profile state; (7) actual Combat entry evaluator must pass deterministic decision parity against DATASET structured rules, beyond schema/adapter compatibility; (8) LONG+SHORT end-to-end dry-run and readiness audit must pass before declaring real-LIVE readiness. Current known gaps at freeze time: DEMO auto-Champion path absent; `LivePromotionControl::publishAutoChampion()` exists but no active caller was found; AUTO/MANUAL central state-machine not yet wired to Champion updates; SHADOW ranking fairness/evidence hardening remains separate. Existing real LIVE state must not be mutated while implementing or auditing these paths.

DATASET SHADOW league risk/equity metrics added (2026-09-08): persistent per-profile accumulation now records `avg_pnl_per_resolved`, `equity_peak_pnl`, `current_drawdown_pnl`, and `max_drawdown_pnl` in addition to cumulative PnL/WR/Traffic. Equity is the cumulative forward-PnL curve sampled at hourly cohort boundaries; drawdown is therefore cohort-boundary drawdown, not intratrade/tick drawdown. Existing profile history is preserved and migration is backward-compatible: on the first post-change cohort, prior cumulative PnL is used as the starting peak baseline. Ranking remains unchanged (raw cumulative PnL, then resolved count) pending separate fairness hardening. Pending PHP 8.4 lint and next matured-cohort runtime verification.

Combat LIVE profile hot-swap guard added (2026-09-08): `strategy-live-control.php` now server-side rejects all profile-changing publication paths (`publish_demo`, manual `publish_profile`, and `approve_pending`) whenever `operator_live_enabled=true`. The operator must explicitly switch LIVE execution OFF before changing the immutable LIVE profile revision, then may inspect/publish the snapshot and separately switch LIVE ON again. The profile tab also renders an explicit blocking warning while LIVE is ON. This closes the observed path where `LivePromotionControl::publish()` preserved a pre-existing ON state and a newly published profile could become effective immediately. Pending PHP 8.4 lint and browser behavior verification.

Combat DATASET SHADOW real operator publication observed (2026-09-08): SHORT `SHORT_ROLL24H_G1_20260908_0552` was manually published through LIVE Control as immutable revision 3, package v1.11.0, while DEMO remained `SHORT ORIGINAL`. The UI correctly exposes both DATASET SHORT G1/G2 options. Important safety finding: although the publication notice states LIVE execution was not enabled by the publication action, the post-publication page shows LIVE=ON and Submit=READY because `LivePromotionControl::publish()` intentionally preserves the previous `operator_live_enabled` state. Therefore profile publication does not toggle LIVE, but publishing while LIVE was already ON can make the newly published snapshot immediately effective for new real entries. This must not be described as 'execution remains OFF' unless pre-state was OFF; future UI/guard hardening should require LIVE OFF before manual profile publication or require explicit confirmation of hot-swap semantics.

Combat DATASET SHADOW LIVE-options UI contract runtime PASS (2026-09-08): PHP 8.4 lint clean and audit returned ok=true. Combat resolves exactly 2 DATASET manual LIVE options per side: LONG G2 rank 1 + G1 rank 2; SHORT G1 rank 1 + G2 rank 2. Every option passed operator_selectable, DATASET source, strategy binding, structured conditions and risk checks. Runtime confirmed mutation=false, live_write_invoked=false and live_execution_invoked=false. DATASET SHADOW profiles are therefore read-only verified through the actual LIVE option-construction contract; remaining end-to-end verification is an operator publication of one immutable revision, which changes Combat control state but does not turn LIVE execution on.

Combat DATASET SHADOW LIVE-options UI contract audit added (2026-09-08): `combatsolo.info/tools/dataset_shadow_live_options_audit.php` reproduces the DATASET portion of `strategy-live-control.php` option construction read-only and verifies every offered option is explicitly manual-live-selectable, sourced from DATASET_SHADOW_LEAGUE, bound to the correct Combat strategy, and carries non-empty structured conditions/risk. It performs no control mutation and no LIVE execution. Pending runtime PASS; after that the remaining end-to-end step is operator/browser publication of an immutable revision (state-changing but still not LIVE ON).

Combat DATASET SHADOW LIVE snapshot dry-run runtime PASS (2026-09-08): PHP 8.4 lint clean; all 4 exported profiles (LONG G2/G1, SHORT G1/G2) passed profile id/definition/conditions/hard-stop/floor/arm/gap checks. Reconstructed settings snapshots correctly carry side profile id, stop, floor/lock, PM arm and PM trail gap (LONG gap 5, SHORT gap 3). Runtime explicitly confirmed mutation=false, live_write_invoked=false, live_execution_invoked=false, dataset_write_invoked=false. Manual snapshot adapter is therefore dry-run verified; this is still not an actual operator publication test.

Combat DATASET SHADOW LIVE snapshot dry-run added (2026-09-08): after PHP 8.4 lint and execution-compat audit re-PASS for all 4 DATASET profiles, added `combatsolo.info/tools/dataset_shadow_live_snapshot_dryrun.php`. It reconstructs the exact manual publication adapter inputs read-only (side profile setting plus hard stop, floor/lock, PM arm, PM trail gap and profile definition hash), but does not instantiate/mutate LivePromotionControl and explicitly reports mutation=false, live_write_invoked=false, live_execution_invoked=false, dataset_write_invoked=false. Pending runtime PASS before operator publication path is considered verified.

Combat DATASET SHADOW manual LIVE-profile option wiring (2026-09-08): execution-compatibility audit runtime PASS for all 4 exported profiles (LONG G2/G1, SHORT G1/G2), with definition/strategy/conditions/risk/operator-selectable/condition-contract/risk-contract all true and no writes. `strategy-live-control.php` now imports DATASET SHADOW profiles only when snapshot marks `manual_live_selectable=true`, converts them through the audited consumer adapter, and adds them to operator `liveProfileOptions`. Manual `publish_profile` therefore may create the existing immutable LIVE snapshot for a selected DATASET profile; this does NOT enable LIVE execution and does NOT change DEMO. DATASET PM aliases now map `trailing_arm_roi_pct` to `pm_arm_roi_pct` and `trailing_gap_roi_pct` to `pm_trail_gap_roi_pct` during publication. Existing separate `toggle_live` remains the only operator LIVE ON/OFF control. Pending PHP 8.4 lint/UI verification before considering this operator path runtime-qualified.

Combat DATASET SHADOW execution-compatibility audit added (2026-09-08): `combatsolo.info/tools/dataset_shadow_execution_compat_audit.php` read-only validates every currently exported LONG/SHORT league profile through `DatasetShadowLeagueConsumer::executionDefinition()`: strategy binding, non-empty structured conditions, supported current feature/op contract (`ret_24h_pct`, `ret_1h_pct`, `range_5m_pct`, `turnover_5m`; <=/>=), complete numeric PM risk fields, and operator-selectable adapter shape. Audit explicitly performs no DATASET write and no LIVE write. Scope is formula/risk adapter contract only, not exchange/live-order execution. Pending PHP 8.4 lint and runtime PASS before DATASET profiles are offered in Combat LIVE profile options.

Combat read-only DATASET SHADOW league consumer added (2026-09-08): new `combatsolo.info/engine/strategies/dataset_shadow_league_consumer.php` reads only DATASET side-local `shadow_league/latest.json`, validates schema/side, and exposes execution formula/risk conversion without writing DATASET or changing LIVE. SHORT `strategy-shadow.php` and LONG `strategy-long-shadow.php` now include a DATASET persistent league table with rank, hours, W/L, WR, Traffic, resolved, cumulative forward PnL, and SHADOW state. This stage is deliberately display-only: DATASET profiles are not yet inserted into Combat LIVE options, no PENDING publication button is enabled for them, and LIVE ON/OFF is untouched. Manual LIVE selection is deferred until execution-compatibility verification of the formula/risk adapter. Pending PHP 8.4 lint/UI runtime verification.

DATASET persistent SHADOW league central-cron runtime PASS (2026-09-08): PHP 8.4 lint passed for `cron_task_registry.php` and `cron_task_executor.php`; real `cron.php` returned `DATASET_CRON_TICK_PASS`, no errors, and executed `profile_shadow_league` through the central scheduler with done=2/2. LONG and SHORT both returned `ALREADY_EVALUATED` for hour_ts 1788854400, proving idempotent same-hour protection after the earlier manual league evaluation. Handler explicitly reported production_runner_invoked=false, combat_write_invoked=false, live_execution_invoked=false; production `profile_runner` remained `DISABLED`. Central hourly SHADOW wiring is therefore runtime-verified.

DATASET persistent SHADOW league central-cron wiring (2026-09-08): added hourly `profile_shadow_league` registry task (3600s, DATASET parser2/orderbook/coin-passport dependencies), dedicated `profileShadowLeague` executor with DATASET ownership/source checks and qualification safety gates, and enabled only this SHADOW league task in cron-center state. Handler evaluates LONG+SHORT through `DatasetProfileShadowLeagueV1`, reports progress 2/2, and explicitly returns production_runner_invoked=false, combat_write_invoked=false, live_execution_invoked=false. Production `profile_runner` remains untouched/disabled. Cron wiring is pending PHP 8.4 lint and first central-cron execution verification before being considered runtime-qualified.

DATASET persistent SHADOW league first runtime PASS (2026-09-08): PHP 8.4 lint passed for league and manual driver. First mature cohort `[1788850800,1788854400]` used `COMBAT_MATCH_CONFIRMED_5M_PARITY_V1`. LONG: 10 base signals; G2 rank1 = 5 allowed/resolved, 4W/1L, WR80%, Traffic50%, forward PnL +14.0318302387; G1 rank2 = 4 resolved, 3W/1L, WR75%, Traffic40%, PnL +5.6528713497. SHORT: 39 base signals; G1 rank1 = 16 resolved, 11W/5L, WR68.75%, Traffic41.0256%, PnL +29.9090207847; G2 rank2 = 14 resolved, 8W/6L, WR57.1429%, Traffic35.8974%, PnL +21.3178887639. Both side snapshots mark all profiles `SHADOW_ACTIVE`, carry execution formula/risk, `manual_live_selectable=true`, while `live_eligible=false`, `combat_publish=false`, and manual tool reported production runner/Combat write/LIVE execution all false. This is one hourly cohort only, not sufficient production evidence.

DATASET persistent SHADOW league foundation added (2026-09-08): `modules/profile_runner/shadow_league_v1.php` evaluates every profile already present in the DATASET immutable profile library on the same newly-matured hourly base-signal cohort, accumulates per-profile base/allowed/resolved/wins/losses/forward-PnL/Traffic/WR/hours, keeps every profile `SHADOW_ACTIVE`, ranks by cumulative forward PnL (resolved as tie-break), and writes side-local `shadow_league/latest.json` plus per-profile cumulative artifacts. Snapshot includes execution formula/risk so a later Combat consumer can expose profiles for explicit manual LIVE snapshot selection. The league itself never writes Combat, never publishes production Champion, and never enables LIVE. Qualification-only manual driver `tools/profile_runner_shadow_league_once.php` hard-fails unless production/promotion/Combat-publish gates remain off. Initial implementation is manual-only pending lint/runtime verification and later central-cron wiring. Combat UI/consumer wiring is deliberately not enabled yet; existing Combat LIVE publication remains operator-controlled and LIVE ON/OFF remains separate.

DATASET SHORT G1 common-forward evaluated (2026-09-08): the manual output no longer listed SHORT G1 as pending because it had matured and persisted as `SHADOW_EVALUATED`. Persisted selector used the hardened gate and returned `KEEP_CHAMPION_DISCARD_CHALLENGER`: G0 forward PnL 36.7886222185 versus SHORT G1 11.3876766223; challenger resolved 8 trades with 44.4444% Traffic, therefore it cleared the minimal evidence gate (>=5 resolved, >=10% Traffic) but lost on the configured TOTAL_FORWARD_PNL objective. No SHADOW promotion was applied. LONG G2 and SHORT G2 remain waiting until 1788858542/1788858549. Note: SHORT G1 training artifact predates base-signal-parity gating (129168 training rows), while its common-forward evaluation is on the current causal/parity path; do not use G1 training row counts as comparable evidence to G2.

DATASET selector hardening verified + maturity observation (2026-09-08): read-only reselection changed historical LONG G1 interpretation from `PROMOTE_SHADOW` to `INSUFFICIENT_COMMON_FORWARD / CHALLENGER_EVIDENCE_GATE` because challenger evidence was only 1 resolved trade and 7.1429% Traffic versus gates 5 and 10%. A subsequent qualification-only common-forward pass invoked neither production runner nor generation build and evaluated nothing new: LONG G2 waits until 1788858542; SHORT G1 waits until 1788853931; SHORT G2 waits until 1788858549. Historical persisted LONG G1 report/shadow artifact remains untouched by the read-only audit.

DATASET common-forward selector read-only reselection audit added (2026-09-08): `tools/profile_runner_common_forward_reselect_once.php` reads persisted common-forward reports and prints old selector vs the current evidence-gated selector without mutating reports, challenger status, library, shadow champion, production Champion, or Combat. This provides a safe way to invalidate interpretation of pre-gate SHADOW decisions such as LONG G1 while preserving immutable historical evidence.

DATASET common-forward selector evidence gate hardened (2026-09-08): `DatasetProfileHourlyPipelineV1::decision()` now refuses SHADOW promotion unless challenger has at least 5 resolved common-forward trades and at least 10% natural Traffic, in addition to exact non-empty common base-signal parity. Failed evidence returns `INSUFFICIENT_COMMON_FORWARD` with explicit reason/threshold fields. These are deliberately minimal anti-one-trade gates for qualification, not a claim of statistical confidence or production readiness. Existing LONG G1 SHADOW publication from the pre-gate selector is historical and must not be treated as qualified; production/Combat publication remains off.

DATASET first common-forward result (2026-09-08): manual qualification evaluator safely ran with production/generation build both false. LONG G1 matured and was evaluated against `COMBAT_LONG_ORIGINAL_G0` on the exact same 14 base signals. G0: allowed/resolved 14/14, 8W/6L, WR 57.1429%, Traffic 100%, forward PnL -8.1340. LONG G1: allowed/resolved 1/1, 0W/1L, WR 0%, Traffic 7.1429%, forward PnL -5.1035. Current selector therefore returned `PROMOTE_SHADOW` solely because challenger PnL (-5.1035) was numerically greater than incumbent (-8.1340); publication was SHADOW-only (`live_eligible=false`, `combat_publish=false`). Evidence is extremely thin (one allowed challenger trade), so this is not sufficient production qualification and exposes a selector-hardening need: minimum challenger resolved/traffic/confidence gates before a SHADOW win. LONG G2 and SHORT G1/G2 were still waiting maturity at this invocation.

DATASET qualification-only common-forward manual evaluator added (2026-09-08): `tools/profile_runner_common_forward_once.php` invokes only `evaluatePending()` for LONG/SHORT using DATASET-local sources. It does not invoke rolling generation/build or the production runner and hard-fails unless dry-run is enabled while production/promotion/Combat-publish gates are false. Intended to evaluate already-built G1/G2 challengers as soon as their causal forward windows mature without waiting for the hourly qualification cadence. Shadow selector/library behavior remains the existing pipeline behavior; production Champion/Combat publication remain disabled.

DATASET G2 qualification build COMPLETE (2026-09-08): bounded manual resumes progressed through 320, 400, 480 and final 519/519 without invoking the production runner. Both G2 challengers reached `BUILT_WAITING_COMMON_FORWARD_EVIDENCE` and formula search completed with `promotion_eligible=false`. LONG: 85 causal base-signal rows; formula `ret_24h_pct >= -7.5535665852997091 AND ret_1h_pct <= -3.7599272005294559`, stop -30, floor +5, arm +10; training 36W/7L, WR 83.7209%, PnL proxy 120.1715, Traffic 50.5882%, 49,589 candidates. SHORT: 531 causal rows; formula `ret_24h_pct <= 4.4859813084112243 AND ret_1h_pct <= 2.9783393501805033 AND range_5m_pct >= 0.57581573896353933`, stop -50, floor +5, arm +10; training 138W/72L, WR 65.7143%, PnL proxy 241.3844, Traffic 39.5480%, 54,842 candidates. These are training/search metrics only; no Champion claim until common-forward evidence. Production/promotion/Combat publication remain off.

DATASET G2 bounded manual resume verified at 240/519 (2026-09-08): first successful qualification-only manual resume advanced both existing G2 challengers exactly one configured 80-symbol batch, 160 -> 240, with `production_runner_invoked=false`. LONG accumulated 45 causal rows; SHORT 231; both remain `BUILD_IN_PROGRESS`, generation 2, base-signal contract `COMBAT_MATCH_CONFIRMED_5M_PARITY_V1`, formula not yet run. This validates the manual tool does not create a new generation and respects the bounded resume step.

DATASET qualification manual-resume bootstrap fix (2026-09-08): first invocation safely failed before any resume with `SOURCE_LAYOUT_MISSING`; bootstrap exposes SourceLayout as service key `layout`, not `source_layout`. Tool corrected to use `$services['layout']`. No G2 state/chunks were modified by the failed invocation.

DATASET qualification bounded manual resume tool added (2026-09-08): `tools/profile_runner_qualification_resume_once.php` advances only an already-existing `BUILD_REQUIRED`/`BUILD_IN_PROGRESS` qualification challenger by one configured bounded batch per side. It does not call rolling-engine generation creation, central production runner, selector/promotion or Combat publication. It hard-fails unless dry-run is enabled and production/promotion/publish gates remain false. Intended to finish a frozen qualification generation without waiting for the hourly cron cadence; normal central qualification cron remains unchanged.

DATASET G2 qualification observed on hardened pipeline (2026-09-08): fresh LONG/SHORT G2 challengers exist and remain `BUILD_IN_PROGRESS`; both use DATASET-local sources and stamp `COMBAT_MATCH_CONFIRMED_5M_PARITY_V1`. Frozen universe is 519 symbols. Two bounded 80-symbol chunks have been persisted per side (`0..80`, `80..160`), manifests are at monotonic `next_symbol_offset=160`; LONG accumulated 28 causal signal rows and SHORT 152 through 160 symbols. This confirms bounded/resumable chunk execution is active rather than the old full-universe one-shot path. The two chunks came from two qualification invocations (initial G2 creation plus the later 07:10 resume), not one 160-symbol batch. Production runner remains disabled; no promotion/live publication is enabled.

DATASET qualification resume/chunk integrity audit PASS (2026-09-08 07:08 UTC): after the monotonic offset fix, PHP 8.4 lint passed and all 8 isolated invariants passed: manifest schema, frozen universe, universe SHA-256, monotonic next offset, chunk count/order, idempotent retry and exact row reconstruction; overall `ok=true`. Resume/chunk infrastructure is cleared for a fresh qualification generation. Production profile runner remains disabled.

DATASET qualification retry monotonicity fix (2026-09-08 07:07 UTC): first resume/chunk audit correctly caught `next_offset=false`. Root cause: idempotent retry of an older already-written batch rewrote manifest `next_symbol_offset` backwards (4 -> 2), even though chunk deduplication and row reconstruction were correct. `DatasetProfileBuildChunkStoreV1::append()` now makes manifest progress monotonic with `max(existing_next_offset, batch_next_offset)`. This prevents stale/retried batch completion from rewinding qualification progress. Audit must be rerun before G2.

DATASET qualification resume/chunk audit added (2026-09-08): `tools/profile_runner_resume_chunk_integrity_audit.php` now exercises the chunk store in an isolated temporary tree before G2: manifest schema, frozen universe exactness/hash, resumed next offset, deterministic chunk ordering/count, idempotent retry of an already-written immutable chunk, and exact ordered row reconstruction. It has no production state side effects and exits nonzero on any failed invariant.

DATASET qualification resume integrity hardening II (2026-09-08): qualification accumulation moved from one ever-growing cursor `rows[]` rewrite to immutable per-batch chunk files under `<side>/build_chunks/<profile_id>/` plus a compact manifest containing frozen universe, SHA-256, next offset and ordered chunk names. Cursor v2 is now small metadata pointing at the manifest. Final formula assembly reads the immutable chunks in manifest order. Legacy cursor universe/offset fallback remains for recovery, while new batches immediately use chunk storage. Production runner remains disabled.

DATASET qualification resume integrity hardening I (2026-09-08): dry-run build cursors now freeze the sorted DATASET Parser2 symbol universe on first batch and persist both the exact symbol list and SHA-256, so later batches cannot shift offsets when symbols are added/removed. The causal builder accepts an explicit frozen symbol list. `resumeBuild()` now sorts challenger files deterministically and recovers both `BUILD_REQUIRED` and `BUILD_IN_PROGRESS`, closing the crash window between challenger creation and first batch. Production runner remains disabled. Large accumulated-row cursor storage is still a known follow-up and is not yet claimed fixed.

DATASET PM semantic audit PASS (2026-09-08 06:59 UTC): corrected audit rerun passed all 8 deterministic cases across LONG/SHORT: hard stop, profit floor, native-trailing-or-floor and horizon MTM; overall `ok=true` under `COMBAT_PM_ROI_PARITY_V1`. This certifies the frozen conservative confirmed-5m ROI-level PM semantics used by common-forward, not exchange tick/latency/native intra-candle execution parity.

DATASET PM semantic audit first run (2026-09-08 06:57 UTC): lint passed, hard-stop/floor/MTM cases passed on both sides, but the original trailing fixture failed on LONG and SHORT (`HORIZON_MTM` 10 instead of expected trailing 11). Inspection showed the fixture itself never retraced through the dynamic `peak-gap` level: after peak 14 and gap 3, protection is 11 while the test candle adverse was 10 only in the same candle that established the peak; conservative ordering intentionally checks existing protection before favorable movement. The audit fixture was corrected to establish the 14 peak in one candle and cross 11 in a subsequent candle. No production PM logic was weakened to make the test pass.

DATASET PM contract hardening (2026-09-08): introduced frozen `COMBAT_PM_ROI_PARITY_V1` in `modules/profile_runner/pm_contract_v1.php`. Common-forward now normalizes profile risk through this contract, separating protection trigger, guaranteed floor, trailing arm and trailing gap aliases used by Combat, and the PM evaluator delegates to the frozen contract. Reports stamp the PM contract. Added deterministic `tools/profile_runner_pm_contract_audit.php` covering hard stop, floor, trailing and horizon MTM for both sides. Scope is explicitly ROI-level conservative confirmed-5m semantics; exchange tick rounding, API latency, mark-price polling, rescue/top-up behavior and exchange-native intra-candle trailing cannot be proven from OHLC and are not claimed as parity. Production remains disabled.

DATASET base-signal parity audit PASS (2026-09-08 06:51 UTC): PHP 8.4 lint passed for the parity contract, causal builder and audit tool. Executable audit over 80 deterministic symbols / trailing 24h produced exact LONG and SHORT `symbol|ts` equality against current Combat package matchers: LONG 9 vs 9 signals, SHORT 82 vs 82, zero only-Combat and zero only-DATASET mismatches, overall `ok=true`. Base-signal cohort parity is therefore empirically verified for this audit window; this does not yet certify PM/execution parity.

DATASET base-signal executable parity audit (2026-09-08): extracted the frozen DATASET-local matcher contract to `modules/profile_runner/base_signal_parity_v1.php` and added `tools/profile_runner_base_signal_parity_audit.php`. The audit is analysis-only: on the same DATASET Parser2 bars it executes the current Combat package `matchConfirmed5m` and the frozen DATASET matcher independently for LONG and SHORT, compares exact `symbol|ts` sets, writes `storage/profile_runner/base_signal_parity_audit.json`, and exits non-zero on any mismatch. The production profile runner remains disabled.

DATASET profile base-signal parity correction (2026-09-08): causal training/common-forward row generation no longer treats every eligible 5m bar as a strategy signal. The DATASET-local causal builder now gates rows through copied, explicit parity logic for the current Combat `matchConfirmed5m` contracts: LONG uses the validated F4_R50_B2 exhaustion matcher (`previous 60m return <= -5%`, green reclaim above previous/t-10m closes with contiguous bars); SHORT uses the current exhaustion matcher (`ret15 >= 1.20`, 12-bar deviation >= 0.80, prior-high breakout and red reversal below prior midpoint/close with 25 contiguous bars). No Combat runtime files are read by DATASET at execution time; the parity contract is version-stamped `COMBAT_MATCH_CONFIRMED_5M_PARITY_V1`. This removes the largest known scientific contamination in base-signal counts, but entry risk filters, turnover eligibility, execution drift/cooldown/reservation and exact tick execution are intentionally not part of the base-signal definition.

DATASET qualification persisted resumable batching (2026-09-08): dry-run challenger builds now persist a per-challenger build cursor under `storage/profile_runner/<side>/build_cursor/`, including accumulated causal rows and the next deterministic sorted-symbol offset. Each qualification tick processes at most the configured symbol batch, leaves the challenger `BUILD_IN_PROGRESS`, and the next tick resumes that same challenger instead of creating a new generation. Formula search runs only after the full current universe has been scanned; the cursor is then removed. Progress is reported against the full universe. This batching is qualification-only; production remains disabled and uncapped.

DATASET qualification CronCenter heartbeat/recovery hardening (2026-09-08): CronCenter now supplies a guarded heartbeat callback to profile-runner handlers; qualification per-symbol progress refreshes both ProgressStore and `cron_center.json` heartbeat/done/total. A newly acquired OS task lock now starts a fresh run timestamp even when persisted state was left `RUNNING` by a dead prior process, preventing stale `last_start_ts` reuse. Other cron handlers retain their existing signatures. Production profile runner remains disabled.

DATASET qualification granular progress hardening (2026-09-08): causal feature builder now emits progress after every scanned symbol, including symbols skipped for insufficient bars, and the hourly pipeline forwards those callbacks during qualification. The 80-symbol bound is now explicitly dry-run-only; a future production build will not inherit the qualification cap. Cron executor installs this qualification progress reporter only for `profile_runner_qualification`, so the disabled production `profile_runner` does not inherit qualification-specific totals/labels. This improves ProgressStore observability but does not yet mutate CronCenter heartbeat and is not a persisted/resumable multi-batch universe scan. Production promotion and Combat publication remain disabled.

DATASET cron stale semantics fixed (2026-09-08): CronCenter status now uses each registry task's timeout_sec as the RUNNING heartbeat stale threshold (minimum 30s), instead of interval_sec*3. This makes profile_runner_qualification (timeout 180s) visibly STALE when its heartbeat stops, rather than masking a dead/hung invocation for three hours. The lock remains an OS flock owned by the running PID; state/lock files are not forcibly deleted because doing so could permit concurrent qualification writers. No production task was enabled.

DATASET qualification progress wiring (2026-09-08): profile runner service now accepts a progress callback and passes it into the hourly pipeline. The central DATASET executor supplies a qualification progress reporter so LONG/SHORT bounded causal builds are visible in ProgressStore instead of being opaque during formula generation. The already-running pre-change PHP process cannot acquire this wiring retroactively; it applies on the next central invocation. No production promotion or Combat publication was enabled.

DATASET qualification bounded causal build (2026-09-08): first real qualification run exposed a blocking full-universe causal scan: LONG G1 was created at 05:37 UTC but remained BUILD_REQUIRED while the cron heartbeat did not advance and SHORT never started. Causal builder now supports deterministic sorted symbol slicing; qualification configs cap a dry-run build at 80 symbols. Hourly pipeline passes the qualification batch bound before formula generation. This is a qualification/runtime-safety bound, not a production universe decision; production promotion/Combat publication remain disabled.

DATASET qualification task enabled (2026-09-08): `storage/control/cron_center.json` now explicitly enables only the new `profile_runner_qualification` lane for profile qualification; production `profile_runner` remains unenabled. The central cron configuration itself is active. This is still SHADOW/dry-run because both side configs remain production-disabled/unqualified and forbid promotion/Combat publication. A real central tick is required to produce qualification metrics.

DATASET qualification cron entry (2026-09-08): registered disabled-by-default central task `profile_runner_qualification` with hourly interval, 180s timeout and Parser2/orderbook/Coin Passport dependencies. Added fixed executor handler `profileRunnerQualification`, deliberately delegating through the same DATASET SourceLayout ownership guards as production profileRunner. LONG/SHORT configs enable qualification dry-run while remaining `enabled=false`, `qualified_for_dataset=false`, `may_promote=false`, and `may_publish_combat=false`. This creates a DATASET-native execution entry without bypassing the central cron architecture; task state still must be explicitly enabled/run through the DATASET cron center before metrics can exist.

DATASET immutable profile library / SHADOW Champion (2026-09-08): added `profile_library_v1.php`. Every mature evaluated challenger is now archived once as a side-local immutable library entry containing formula, risk, training evidence, common-forward statistics and selector result. A `PROMOTE_SHADOW` recommendation can atomically update only `shadow_champion.json`; the published artifact is explicitly `live_eligible=false` and `combat_publish=false`. This creates the persistent LONG/SHORT profile pool and rolling SHADOW leader needed for recurring market regimes without mutating production `champion.json`, Combat state, or LIVE execution.

DATASET continuous pending orchestration (2026-09-08): hourly profile service now scans each side's persisted challengers before attempting the next generation. `BUILT_WAITING_COMMON_FORWARD_EVIDENCE` challengers younger than creation+2h remain waiting; mature challengers are automatically passed through pair-local common-forward and selector and their status/artifact is persisted. The same tick can then create/build the next due Gx, so maturation and generation overlap instead of serially blocking the hourly rolling window. Incumbent is read from DATASET side `champion.json` when present; until publication is implemented, a side-local G0 execution-risk fallback is constructed from config. No selector result mutates Champion and no LIVE state is touched.

DATASET hourly SHADOW pipeline (2026-09-08): added `hourly_pipeline_v1.php` and wired new challenger builds through it. New Gx is persisted with causal evidence and formula/risk, moving to `BUILT_WAITING_COMMON_FORWARD_EVIDENCE` or `BUILD_REJECTED`. Pipeline also implements the mature pair path: wait until challenger interval `[T,T+1h]` plus its 1h outcome horizon is fully observable, rebuild causal forward rows from DATASET Parser2, load confirmed 5m bars, evaluate incumbent/challenger on the exact same base rows, persist per-challenger common-forward, run selector, and mark `SHADOW_WINNER_NOT_PROMOTED` or `SHADOW_EVALUATED`. Champion mutation and LIVE enable remain impossible in this pipeline. Mature-pending queue scanning is the remaining orchestration hook before a full dry run.

DATASET pair-local common-forward stage (2026-09-08): added `common_forward_collector_v1.php`. It evaluates exactly incumbent Champion + one challenger on the same causal feature-row interval, applies each profile formula independently, resolves allowed signals through the conservative confirmed-5m PM evaluator, and reports equal-base diagnostics, allowed/resolved, WR, natural Traffic and `total_forward_pnl`. Pair maturity is interval end + 1h outcome horizon; reports are SHADOW-only and `promotion_applied=false`. Rolling engine can persist one common-forward artifact per challenger. No retrospective future challenger is added to an older pair, avoiding the earlier SOLO contamination class. Full hourly orchestration remains gated before production enablement.

DATASET formula/PM parity stage (2026-09-08): upgraded `formula_builder_v1.php` from one-condition baseline to beam search across 1..5 unique-feature conditions (q20/q40/q60/q80 atoms, configurable beam width, stop grid) while keeping training `promotion_eligible=false`. Added `pm_evaluator_v1.php`: conservative confirmed-5m OHLC execution path for LONG/SHORT at 5x, checking hard stop/existing protection before favorable same-candle movement, +5 floor, +10 trailing arm, horizon MTM, and side-specific trailing gap (SHORT +3, LONG +5). Configs now explicitly carry these gap values. PM evaluator exists as the forward parity primitive but is not yet wired to promotion; exact common-forward collection remains the next gate.

DATASET causal builder baseline (2026-09-08): added DATASET-native `causal_feature_builder_v1.php` reading confirmed local Parser2 native 5m bars only. Features are strictly at-or-before signal time (1h/24h return, current 5m range/volume/turnover); the following 1h is isolated as label-only MFE/MAE/end ROI with LONG/SHORT direction and 5x ROI scaling. Added `formula_builder_v1.php` baseline threshold search over q20/q40/q60/q80 and stop grid, reporting WR/Traffic/PnL proxy. Current formula search implements one condition while preserving the 1..5 target contract and explicitly marks `promotion_eligible=false`; it is diagnostic until multi-condition search plus execution-path PM parity are complete. Service wires this build only after an enabled+qualified side creates a challenger.

DATASET native rolling lifecycle skeleton (2026-09-08): added `modules/profile_runner/rolling_engine_v1.php`, shared by LONG/SHORT. Once a side is explicitly enabled and its LAB algorithm marked qualified, the DATASET hourly task can create a causal rolling-24h challenger generation with a 1h evaluation horizon, persist side runner state/queue atomically, and retain incumbent Champion identity. Added SHADOW selector primitive requiring equal nonzero common-forward base signals and comparing total forward PnL; it never applies promotion. The lifecycle intentionally stops challengers at `BUILD_REQUIRED` until DATASET-native causal feature-row construction and execution-path-compatible forward PM evaluation are qualified, preventing placeholder training from becoming production evidence.

DATASET profile-runner ownership enforcement (2026-09-08): corrected the staged service to consume paths from Dataset `SourceLayout` instead of guessed storage paths. The cron executor now requires DATASET ownership for Parser2, Coin Passport and Wave before invoking profile production and passes their resolved local paths into the profile runner. This prevents mixed-cutover SourceLayout from silently handing the production profile runner a SOLO path. Correct local defaults are `parser2_market_cache/storage`, `coin_passport`, and `wave_engine` under DATASET localDataRoot.

DATASET profile-runner source isolation (2026-09-08): production service now declares/validates DATASET-local Parser2, Coin Passport and Wave storage contracts and explicitly records `solo_runtime_dependency=false`; it does not read SOLO or Combat research artifacts. LONG/SHORT configs now carry versioned LAB algorithm references plus `qualified_for_dataset=false` and `enabled=false`. Production generation is therefore blocked until a LAB algorithm is explicitly qualified and enabled; migration means copying/versioning the algorithm contract into DATASET, not making DATASET depend on SOLO runtime files.

DATASET profile-runner ownership staging (2026-09-08): corrected production placement: SOLO remains LAB/reference only; DATASET owns production profile generation/library/Champion publication; Combat remains consumer only. Added DATASET `profile_runner_service.php`, staged LONG config alongside SHORT, and registered hourly `profile_runner` in DATASET central cron with Parser2 market/orderbook + Coin Passport dependencies. Both LONG/SHORT side configs remain disabled/STAGED_NOT_SCHEDULED and promotion/LIVE auto-enable remain false until LAB common-forward + execution-path parity gates pass. This establishes the production slot without starting duplicate or unqualified profile generation.

LONG common-forward stage (2026-09-08): added `long_forward_collector_v1.php` and wired it before the selector in `long_runner_hourly_pipeline_v1.php`. Every built challenger gets its own exact 1h common interval containing only the frozen incumbent Champion and that challenger; selector requires identical nonzero base-signal counts and at least one resolved allowed trade across the pair, then recommends `PROMOTE_SHADOW` only when challenger TOTAL_FORWARD_PNL is greater, otherwise `KEEP_CHAMPION_DISCARD_CHALLENGER`. Intervals mature only after the 1h interval plus the 1h outcome horizon. Current LONG collector uses the existing causal G8 trajectory proxy rather than tick/execution-perfect PM, so all decisions remain SHADOW recommendations: promotion/state mutation/LIVE publication are disabled pending execution-path parity validation.

LONG rolling runner build stage (2026-09-08): added `long_profile_challenger_builder_v1.php`, `long_shadow_selector_v1.php`, and unified `long_runner_hourly_pipeline_v1.php`; the LONG cron lane now invokes the unified pipeline rather than the coordinator alone. On a due tick it creates a rolling-24h challenger, refreshes causal LONG G8 rows, builds a 1-5-condition candidate with STOP grid -3..-50 / Floor +5 / Arm +10 and natural Traffic observation, then runs a SHADOW-only selector. The selector is already challenger-specific and compares TOTAL_FORWARD_PNL, but deliberately returns no actionable decision until a dedicated `long_common_forward_v1.json` collector exists. Promotion/state mutation/LIVE auto-enable remain false. Thus runner+causal-refresh+builder+selector mechanics exist; common-forward collection remains the missing stage before scientific KEEP/DISCARD decisions.

LONG rolling runner bootstrap (2026-09-08): added SOLO LAB `tools/long_profile_runner_v1.php` and `long_runner_cron_lane_v1.php`, wired into `public/cron.php`. The lane creates one causal rolling-24h LONG challenger record per hourly cadence from the existing LONG G8 causal research contract, with 1-5-condition builder contract, STOP grid -3..-50, Floor +5, Arm +10, natural Traffic observation, and explicit SHADOW-only/no-training-promotion/no-LIVE-auto-enable safety. This is runner/bootstrap generation only: builder/common-forward/selector parity is not yet implemented, so generated LONG challengers remain `BUILD_REQUIRED` and cannot replace G0. Public cron now reports `long_profile_runner` separately. External cron invocation still requires runtime confirmation.

LONG profile visibility audit (2026-09-08): verified that Combat LONG is actively collecting `long_profile_lab_stats.json`, but only `COMBAT_LONG_ORIGINAL_G0` exists there; SOLO `history_fingerprint_lab` currently has no LONG rolling runner implementation/generations (only the frozen `LONG_ORIGINAL_24H_G0.json` seed). Therefore Combat must not pretend new LONG generations exist. `strategy-long-shadow.php` now explicitly labels the fallback registry `ROLLING_RUNNER_NOT_CONNECTED`, states that Combat currently has only the qualified G0, and carries G0 STOP/Floor/Arm metadata from `control/long_original_g0.json` for the future profile view. No legacy LONG registry/autobuilder was revived and no LIVE behavior changed.

SOLO SHORT hourly scheduler diagnosis/fix (2026-09-08): rolling state was stale at G3 since 2026-09-07 13:36 UTC and common-forward since 13:50 UTC while Combat continued trading, proving the LAB hourly lane was not executing. `short_runner_cron_lane_v1.php` no longer blindly uses `PHP_BINARY` under web/FPM; it now selects a real CLI PHP (`/opt/php84/bin/php` preferred, then standard paths) and reports `php_cli_missing` explicitly if unavailable. Existing `public/cron.php` integration remains unchanged. Guarded `solo_atomic_runner` was intentionally NOT revived: its forced test remains blocked by `SOLO_WAVE_STOPPED_FOR_CUTOVER`, which belongs to the retired/cutover writer path and must not be re-enabled just to run LAB. External/public cron invocation still requires runtime confirmation; this patch fixes the web-SAPI child-process hazard but does not by itself prove the host scheduler is calling `public/cron.php`.

LONG G0 execution compatibility fix: `exhaustion_long_demo_runner.php::longShadowProfiles()` now materializes authoritative `control/long_original_g0.json` into the runner's executable profile-definition schema (`structured_rules` + `regime_all`, G8 feature schema). This removes the false `ENTRY_SKIPPED_PROFILE_NOT_EXECUTION_COMPATIBLE` condition caused by the retired/missing legacy `long_shadow_profiles.json` registry. G0 remains the selected Champion; signals still must pass its regime/rules evaluation before entry, and no LIVE enablement is changed.

SHORT Rolling UI follow-up: Combat now always surfaces the authoritative frozen `control/short_original_g0.json` as the current SHORT Champion instead of showing an empty bridge panel before any qualified challenger has arrived. The table shows G0 evidence/risk and any later bridge-qualified profiles, while raw SOLO LAB G1/G2/G3 remain intentionally absent until qualification; Combat therefore stays a consumer rather than reading LAB state directly.

LONG SHADOW render follow-up: the shared dispatcher replacement had also removed `sh_money()` and `sh_pct()`, while the included LONG view still calls both helpers. They were restored in `strategy-shadow.php`; this explains the partial LONG render stopping after the first status cards despite the HTTP route itself loading.

LONG SHADOW follow-up: the old `long_shadow_profiles.json` registry is absent, so the LONG page now falls back to the authoritative frozen `control/long_original_g0.json` runner seed instead of rendering an empty legacy registry. The fallback exposes LONG ORIGINAL G0 as non-selectable reference evidence; no old LONG profile library is recreated and no LIVE/DEMO selection is enabled from the fallback.

Follow-up fix: the replacement `strategy-shadow.php` keeps the shared LONG dispatcher and therefore must preload `StrategyPackageManager`, `StrategyRuntimeControl`, and `LivePromotionControl` before including `strategy-long-shadow.php`; these dependencies were restored after the LONG SHADOW route returned HTTP 500.

The old Combat SHORT profile-lab stack is no longer an active profile-selection system. `exhaustion_short_demo_runner.php` no longer executes ShortProfileLab or the legacy 10L/V2/robust/geo/phase/win-protect autobuilders; their returned status is `RETIRED_ROLLING_RUNNER_REPLACEMENT`. `strategy-shadow.php` was replaced by a read-only Rolling Profile Runner status surface: Combat is explicitly a Champion consumer, while SOLO LAB owns current research and DATASET is the staged production owner. Historical legacy profile-lab files/backups are retained for audit and were not deleted. Existing open-position ownership is not rewritten. Automatic Champion publication and LIVE execution remain disabled.

## 2026-09-07 — SHORT common intervals restricted to incumbent-vs-owner pair

Removed retrospective contamination from `common_intervals`: a challenger's interval now contains only the incumbent Champion snapshot and that interval's owning challenger. Future/later challenger formulas are no longer evaluated inside an earlier challenger's comparison interval. The collector also guards interval accumulation with explicit profile membership. Aggregate profile-lifetime diagnostics remain available separately under `profiles`, but selector evidence is pair-local only.

## 2026-09-07 — SHORT selector now compares exact challenger-specific common intervals

Collector now emits `common_intervals` for every pending challenger. For a challenger created at T, all profiles (incumbent G0 and every challenger formula) are replayed over the same base-signal interval T..T+1h; the interval is considered mature only after T+2h so every signal near the end has its full configured 1h outcome observable. Selector consumes the oldest matured interval, verifies identical nonzero base-signal counts, requires at least one resolved allowed trade across the pair, then compares TOTAL_FORWARD_PNL. This removes the prior unfair comparison between profile-local lifetimes. Decisions remain SHADOW-only and do not mutate Champion/Combat/LIVE.

## 2026-09-07 — SHORT SHADOW selector wired into hourly pipeline

Added `short_shadow_selector_v1.php` after the common-forward collector. It emits one of `INSUFFICIENT_EVIDENCE`, `KEEP_CHAMPION_DISCARD_CHALLENGER`, or `PROMOTE_SHADOW`, with TOTAL_FORWARD_PNL as the primary metric and Traffic reported observationally. It is deliberately SHADOW-only: it cannot publish to Combat, change LIVE execution, or mutate the production Champion. A strict parity gate currently refuses comparison unless both incumbent and matured challenger have nonzero resolved evidence and equal base-signal sample counts. This exposes the remaining collector requirement explicitly: incumbent and challenger must be recomputed on the exact same challenger-specific interval before automatic keep/discard can be trusted. Hourly pipeline now reports the selector result while all promotion safety flags remain false.

## 2026-09-07 — Combat execution restored side-local bans and aligned SHORT fallback PM

Fixed the execution regression created when legacy `globalBlockedMap()` was disabled: SHORT scan now reads `blockedMap('exhaustion_short_120m_v1')` and LONG scan reads `blockedMap('exhaustion_long_50_f4_r50_b2_v1')`, so a SHORT ban cannot block LONG and vice versa. The strategy UI blocked-map view was changed to the selected strategy's local map as well. SHORT package fallback defaults were aligned with the current G0/profile risk contract: hard stop -35 ROI, profit protection/floor +5, native trailing arm +10, trailing gap 3. Profile/LivePromotion settings still override these defaults, and existing positions retain their entry-time snapshot by policy.

## 2026-09-07 — SHORT forward collector upgraded from +5/STOP proxy to conservative PM path

Replaced the aggregate MFE/MAE `+5 or STOP` forward score with a chronological 5m OHLC evaluator. For each profile it now applies the profile hard stop, +5 ROI profit floor, +10 ROI native-trailing arm, default 3 ROI trailing gap, and marks any still-open position to the horizon close. Because 5m OHLC cannot reveal intrabar ordering, ambiguous candles are deliberately conservative: protection that existed before the candle is checked before allowing that candle's favorable low to arm/raise trailing. Profile risk values are read per Champion/challenger. Promotion remains disabled until this evaluator is compared against actual Combat execution samples; it is substantially closer to runtime PM semantics but is not tick-perfect exchange replay.

## 2026-09-07 — SHORT collector/replay converted to causal 1h keep-or-discard evaluation

Implemented the rolling interpretation in code, not only config. Runner refresh now resolves training evidence to `now - profile_evaluation_horizon_hours` (currently 1h) instead of `now - 24h`; the causal replay accepts `SHORT_OUTCOME_HORIZON_SEC` and refresh invokes it with the configured 1h horizon. The 24h value remains the sliding history/profile window. Forward collector now resolves outcomes after 1h and, critically, each challenger is scored only on signals at/after its own creation timestamp, preventing G2 from receiving G1-era forward observations. Promotion remains disabled pending exact candle-path PM parity; current +5/STOP PnL is still a conservative proxy. Canonical feature parity was checked for the suspicious `eligible_turnover10m` field: despite the legacy/misleading name, the canonical G0 replay itself defines it from summed 24h turnover >= 10m USDT, and the collector matches that definition.

## 2026-09-07 — SHORT runner corrected to hourly sliding 24h profile window

Corrected the runner interpretation after G2 validation: 24h is the rolling profile/history window, not a requirement to wait 24h between profile decisions. SHORT now declares `ROLLING_24H_WINDOW_ENDING_NOW_REBUILT_EVERY_1H`, `window_step_hours=1`, `outcome_label_horizon_hours=0`, and `profile_evaluation_horizon_hours=1`. Each hourly generation therefore uses the newest 24h window (dropping the oldest hour and adding the newest hour), builds a challenger, and the runner can keep/reject it on the configured evaluation cadence. The same contract is mirrored in the staged DATASET production config. This removes the previous T-48h→T-24h lag policy. Exact scoring must remain causal: only outcomes actually observable by each evaluation timestamp may be used; future 24h information must not leak into the hourly decision.

## 2026-09-07 — SHORT LAB runner gained explicit one-shot force path

Added `--force-once` to `short_profile_runner_v1.php` and passthrough in `short_runner_hourly_pipeline_v1.php`. It bypasses only the cadence/due check for one invocation, marks the challenger `forced_once=true`, and then executes the normal matured-corpus refresh -> builder -> common-forward collector path. It does not alter the configured 1h cadence and does not enable promotion or LIVE execution. Intended for immediate G2 mechanical validation without waiting for the next scheduler due time.

## 2026-09-07 — DATASET selected as production owner for profile runner

Architecture fixed: SOLO remains the LAB for replay/research and development of runner revisions; DATASET is the production destination for profile generation, generation/Champion state and profile publication; Combat is the execution consumer. Staged `datasetis.info/modules/profile_runner/` with ownership documentation and a SHORT production config mirroring the current matured-label/hourly/natural-Traffic policy. The DATASET copy is deliberately `STAGED_NOT_SCHEDULED` with promotion disabled: the existing SOLO runner remains the single active LAB reference until the G2 path and common-forward parity/promotion semantics are verified, preventing duplicate generations and split-brain Champion state.

## 2026-09-07 — Combat symbol bans split by LONG/SHORT; reset tool staged

Corrected Combat ban ownership: a coin ban is strategy/side-local, so a SHORT ban must not block the same symbol in LONG and vice versa. Disabled the legacy cross-strategy `globalBlockedMap()` execution surface; execution should consume `blockedMap(strategyId)` only. Added guarded one-shot `combatsolo.info/tools/reset_and_split_symbol_bans_v2.php` to clear current ban entries under the existing lock and rewrite the control DB with `scope=SIDE_STRATEGY_INDEPENDENT`, preserving per-strategy configs. The reset tool is staged but requires one explicit server PHP invocation to mutate the current runtime ban DB; workspace code deployment alone does not claim the runtime bans have already been cleared.

## 2026-09-07 — Combat SHORT Champion receiver and AUTO/MANUAL bridge added

Added Combat-side `engine/strategies/short_runner_bridge.php` as the receiver for an already-qualified SOLO SHORT Champion. The receiver persists the profile in `short_dynamic_profiles.json` with its risk snapshot. In LIVE profile-selection `AUTO`, it atomically republishes the Champion through `LivePromotionControl::publishAutoChampion()` with `short_entry_profile`, hard STOP, +floor and native trailing arm folded into the settings snapshot; it preserves the existing operator LIVE execution flag and never enables execution. In `MANUAL`, the runner Champion becomes a PENDING profile for operator approval instead of replacing LIVE automatically. `strategy-live-control.php` now also exposes/resolves `COMBAT_SHORT_ORIGINAL_G0` alongside manifest and dynamic SHORT profiles. This establishes the Combat receiver contract; SOLO promotion-to-Combat invocation remains gated until common-forward promotion itself is enabled.

## 2026-09-07 — SHORT_PROFILE_RUNNER_V1 connected to SOLO public scheduler

Connected the validated SHORT hourly pipeline to the existing `tredersolo.online/public/cron.php` through isolated lane `modules/history_fingerprint_lab/short_runner_cron_lane_v1.php`. The public scheduler may call the lane frequently, but the lane reads SHORT runner cadence/state first and returns `not_due` without launching the heavy pipeline until `last_build_ts + configured cadence`. When due it executes the consolidated pipeline (generation -> matured DATASET refresh -> build -> common forward). Cron response now exposes `short_profile_runner` and `short_profile_runner_ok`. This does not enable promotion or LIVE execution. The lane uses the existing SOLO public-cron scheduling contour rather than re-enabling legacy CronManager tasks, preserving isolation policy.

## 2026-09-07 — SHORT hourly pipeline consolidated into one idempotent tick

Added SOLO LAB `tools/short_runner_hourly_pipeline_v1.php`. One invocation now runs the SHORT lifecycle in order: runner due-check; if due create next generation; refresh its fully matured causal training corpus; build the challenger; then run the common-forward collector for Champion plus all built pending generations. If runner is not due, no generation is created but forward collection still refreshes. Promotion and LIVE execution remain disabled in this pipeline. Also removed the runner's hard-coded `+3600` next-due display: `next_due_ts` now derives from side-specific cadence config, so later SHORT cadence self-tuning will not leave stale status output. This script is the scheduler target; no cron/systemd installation is claimed by the LAB workspace change itself.

## 2026-09-07 — SHORT common-forward collector added

Added SOLO LAB `tools/short_forward_collector_v1.php` for common subsequent evidence from the challenger's `forward_from_ts`. It reconstructs fresh canonical SHORT base signals directly from DATASET Parser2, applies the same eligible-turnover gate, evaluates frozen `COMBAT_SHORT_ORIGINAL_G0` and all built pending SHORT challengers on the same signal stream, and records base signals, allowed, natural Traffic, open/resolved, W/L, WR and total PnL proxy per profile in `short_common_forward_v1.json`. Outcomes resolve only after their full 24h horizon; collector remains `COLLECTING` and cannot promote. Current PnL scoring is deliberately conservative +5/STOP proxy, not claimed as exact Bybit native trailing parity.

## 2026-09-07 — SHORT supervised window corrected to fully matured T-48h..T-24h

The first refresh exposed the exact label-lag issue: G1 was created at 12:47 UTC with nominal T-24h..T training, but only ~6 minutes of that interval had a complete 24h outcome by refresh time, so it cannot supply a valid supervised 24h build. SHORT config now declares `outcome_label_horizon_hours=24`; `RollingProfileCycleV1::window()` shifts SHORT training to the latest fully matured 24h interval ending at build_time-24h (therefore T-48h..T-24h), while `forward_from_ts` remains the actual build time T. Added `short_g1_rebase_matured_window_v1.php` to repair the already-created G1 artifact/state onto this policy. Hourly rebuild cadence is unchanged; only supervised label timing is corrected.

## 2026-09-07 — SHORT runner causal refresh and 24h label-maturity guard added

First G1 build correctly blocked with 0 eligible rows because the G0 replay corpus ended before the G1 window. Added `tools/short_runner_refresh_causal_v1.php` to refresh the SHORT base-signal/replay corpus directly from DATASET Parser2 for the pending runner window, and added explicit `trajectory.future_complete`/`observed_to_ts` metadata to causal replay rows. Challenger builder now trains only on rows whose full 24h future is complete. This prevents a new hourly profile from accidentally learning from outcomes that do not exist yet. Important consequence: a nominal recent-24h supervised window cannot be fully labelled at creation time with a 24h outcome horizon; newest signals belong to forward/shadow observation, while training must use matured history. Runner must keep training-window semantics distinct from forward collection.

## 2026-09-07 — SHORT G1 causal 24h builder added

Added SOLO LAB `tools/short_profile_challenger_builder_v1.php`. It takes the newest `BUILD_REQUIRED` SHORT challenger, restricts the causal replay corpus strictly to that challenger's frozen 24h train window, searches 1..5-condition formulas and the SHORT STOP grid using training evidence only, records natural Traffic, and writes the selected formula/risk back into the challenger artifact as `BUILT_WAITING_COMMON_FORWARD_EVIDENCE`. It explicitly cannot promote from training. If the current replay corpus does not contain at least 40 eligible rows inside the requested recent 24h window, it fails visibly as `BUILD_BLOCKED_INSUFFICIENT_24H_ROWS` instead of silently training on older data. This is the first real G1 build stage; common subsequent forward scoring remains the next gate.

## 2026-09-07 — Runner Traffic-collapse hypothesis promoted to measured diagnostic

Runner policy remains NO FORCED TRAFFIC CORRIDOR, but every generation must now explicitly test the user's collapse hypothesis: unconstrained WR optimization may monotonically reduce natural Traffic toward tiny samples (e.g. near-100% WR at ~1% Traffic). Treat this as a hypothesis, not an assumption. Record per generation `natural_traffic_pct`, `wr_pct`, `total_forward_pnl`, `resolved_count`, and deltas versus Champion; maintain the WR↔Traffic/PnL frontier across G0/G1/... . Do not reject a low-Traffic challenger solely for low Traffic, but do not select on WR alone: promotion primary remains common-forward TOTAL_FORWARD_PNL. If repeated generations empirically show Traffic collapse while PnL stops improving, the observed frontier becomes the basis for a self-learned corridor/penalty rather than a hand-set quota.

## 2026-09-07 — SHORT_PROFILE_RUNNER_V1 hourly coordinator activated in LAB contract

Added SOLO LAB `tools/short_profile_runner_v1.php`. The independent SHORT runner reads the frozen `COMBAT_SHORT_ORIGINAL_G0`, obeys the side-specific 1h cadence, creates generation-numbered challenger artifacts under `storage/rolling_profile_v1/short_challengers/`, and records runner state/pending history in `short_runner_state_v1.json`. A created challenger is explicitly `BUILD_REQUIRED_FROM_CAUSAL_SHORT_REPLAY`; training score can never promote it. Promotion remains blocked until Champion and Challenger have common subsequent forward evidence, with TOTAL_FORWARD_PNL primary and Traffic observational. SHORT config now records selected G0 STOP -35. This coordinator creates the hourly research job record; it does not itself schedule cron/systemd and does not claim end-to-end Combat AUTO handoff.

## 2026-09-07 — COMBAT_SHORT_ORIGINAL_G0 frozen and installed paused

SHORT STOP selector chose -35 from the robust leader surface: all 15 threshold/drift cases retained >=75% WR + positive PnL, worst PnL +76.168 ROI, median +115.055, worst WR 84.615%, median WR 90.909%. Frozen `COMBAT_SHORT_ORIGINAL_G0` with the 4-condition causal formula, hard STOP -35, +5 floor and +10 native trailing arm. Combat artifact installed at `combatsolo.info/storage/live_01/control/short_original_g0.json` with status `DEMO_PAUSED_RUNNER_SEED`; LIVE execution is explicitly false. SOLO rolling state now seeds independent `SHORT_PROFILE_RUNNER_V1` generation 0 with this champion. Traffic remains observational (source holdout 5.95%), and future replacement requires common forward evidence with total forward PnL as primary metric. Existing-position entry snapshots remain immutable; AUTO profile following, when wired end-to-end, must atomically apply profile + risk to new entries only.

## 2026-09-07 — SHORT robust STOP selector added

Added SOLO LAB `tools/short_g0_stop_selector_v1.php` to choose the SHORT G0 STOP from the already-computed leader stress surface instead of copying the headline -50. It aggregates all 15 threshold/drift stress cases per STOP and ranks -30/-35/-40/-45/-50 by: share retaining >=75% WR + positive PnL, then worst-case PnL, then median PnL, with shallower STOP used only as the final tie-break. Output is `short_g0_stop_selector_v1_report.json`; selected STOP remains pending G0 freeze until this selector is run.

## 2026-09-07 — SHORT G0 robustness gate passed by 4-condition leader

Robustness run on the chronological 420-row holdout strongly separated the leader. Formula `range_24h_pct<=8.883248730964489 AND prev_close_location>=0.46822742474916917 AND prev_turnover_spike12>=1.0236470405864129 AND dist_high_1h_pct<=1.0480174447065087` (source STOP -50) retained positive PnL in 75/75 stress cases and simultaneously retained >=75% WR + positive PnL in 75/75 cases across threshold scales 0.95/1.00/1.05, STOP -30/-35/-40/-45/-50 and adverse SHORT entry drift 0..0.30%. Worst stress PnL remained +64.187 ROI, median +116.658, worst WR 84.615%. Source holdout was 24W/1L=96%, Traffic 5.95%, PnL +163.531. The simpler 2-condition finalist was materially weaker (61.33% of stress cases retained >=75%+positive PnL; worst PnL negative). Leader is now `G0_FREEZE_CANDIDATE`, but final STOP should be selected from the stress surface rather than automatically copying source -50; native trailing remains a 5m proxy limitation.

## 2026-09-07 — SHORT G0 finalist robustness/stress tool added

Added SOLO LAB `tools/short_g0_robustness_v1.php`. It takes distinct >=75% exact-path finalists and stress-tests the untouched chronological holdout across threshold perturbations 0.95/1.00/1.05, independent SHORT STOP neighbours -30/-35/-40/-45/-50, and adverse SHORT entry-reference drift 0/0.05/0.10/0.20/0.30%. Ranking emphasizes the share of stress cases retaining both positive PnL and >=75% WR, then median PnL; natural Traffic remains descriptive. This is the robustness gate before selecting/fixing SHORT ORIGINAL G0.

## 2026-09-07 — SHORT exact replay produced first robust G0 finalists

Exact chronological 5m replay over 1,398 turnover>=10M rows materially changed the proxy result and produced profitable >=75% holdout candidates. Current leader: 4-condition formula `range_24h_pct<=8.883248730964489 AND prev_close_location>=0.46822742474916917 AND prev_turnover_spike12>=1.0236470405864129 AND dist_high_1h_pct<=1.0480174447065087`, STOP -50: train 54/54 wins (100%), natural Traffic 5.52%, total ROI PnL +301.53; holdout 24W/1L=96%, natural Traffic 5.95%, total ROI PnL +163.53, expectancy +6.54 ROI/trade. Secondary simpler 2-condition formula `range_24h_pct<=8.883248730964489 AND prev_turnover_spike12>=1.6518418924702229`, STOP -35: train 49/49 wins, holdout 23W/4L=85.19%, Traffic 6.43%, holdout PnL +56.63. A 4-condition STOP -40 candidate also held 18W/2L=90%, PnL +40.53. These are finalists, NOT frozen G0: sample is still small and signal-close / 5m native-trailing approximations require fill-drift sensitivity and robustness perturbation before promotion. Deep STOP -50 must not be selected merely from headline WR; compare loss magnitude, parameter-neighbour stability and PnL under entry slippage.

## 2026-09-07 — SHORT exact chronological path replay added

Added SOLO LAB `tools/short_g0_exact_path_replay_v1.php` to rescore temporal SHORT candidates candle-by-candle instead of using aggregate MFE/MAE ordering. It applies the current +5 floor / +10 trailing-arm contract, candidate-specific SHORT STOP, 24h horizon and conservative adverse-first handling for same-5m-bar ambiguity. It reports exact chronological research W/L, total ROI PnL, expectancy, natural Traffic and exit reasons separately for train/holdout. Signal-close remains the causal reference and final freeze still requires fill/drift sensitivity; exchange-native intra-bar trailing cannot be perfectly reconstructed from 5m OHLC, so that limitation remains explicit.

## 2026-09-07 — SHORT temporal candidate search exposed conservative-proxy failure

First 70/30 candidate run used 1,398 turnover>=10M rows (978 train / 420 holdout), tested 397 candidates. Some formulas reached >=75% holdout WR (e.g. 17W/4L=80.95% and 16W/4L=80%), but their holdout PnL proxy was negative (-75 / -80) because the aggregate MFE/MAE proxy lets a deep STOP dominate even when +5 was reached earlier. Therefore the apparent high-WR candidates are NOT rejected and NOT promotable from this proxy; the ranking itself is not economically meaningful until exact candle-by-candle first-hit ordering is reconstructed. Next mandatory stage: exact path-dependent +5 floor / +10 native-trailing / STOP-grid replay for the candidate set, including conservative same-bar ambiguity and later fill/drift sensitivity.

## 2026-09-07 — SHORT temporal G0 candidate search added

Added SOLO LAB `tools/short_g0_temporal_candidate_search_v1.php`. It searches only turnover>=10M causal rows, uses a chronological 70/30 train/holdout split, constructs 1..5-condition formulas from TRAIN-only feature quantiles with beam search, searches the SHORT STOP grid independently, ranks final research candidates by holdout total PnL proxy, reports natural Traffic without enforcing a corridor, and marks the exploratory >=75% holdout WR target. Current outcome is deliberately conservative/provisional because the trajectory file stores aggregate MFE/MAE rather than exact first-hit STOP/+5 ordering; any candidate must pass an exact path-dependent replay plus entry fill/drift sensitivity before G0 freeze.

## 2026-09-07 — SHORT full causal replay first result + production timing evidence

Server replay over the 7,616 validated raw SHORT triggers produced 5,131 rows with complete 24h geometry, of which 1,398 satisfy reconstructed rolling turnover >=10M; reach +5=4,715, reach +10=4,170, weak-positive-loss=332. STOP-hit counts decline from 4,735 at -3 to 1,178 at -50. These aggregate reach counts are NOT WR and must not be used for promotion because they are before causal profile selection and before exact PM path resolution. Production ownership evidence confirms the decision occurs after the 5m signal candle closes: example signal_bar_ts 1788780600, signal_close_ts 1788780900, decision_ts 1788780906, opened_ts 1788780907. Actual fill may differ from the signal-close reference, so candidate research may use signal close as a causal reference but final qualification must include entry drift/fill sensitivity.

## 2026-09-07 — SHORT full causal trajectory replay tool added

Added SOLO LAB `tools/short_g0_full_causal_replay_v1.php`. It consumes the independently reproduced 7,616 raw SHORT base signals, reconstructs the existing exact causal geometry feature family from DATASET Parser2, marks rolling 24h turnover >=10M eligibility, and computes 24h SHORT MFE/MAE, reach +5/+10, weak-positive-loss and STOP-hit counts for the -3..-50 grid. Current entry price is deliberately labelled `SIGNAL_CLOSE_RESEARCH_ANCHOR_PENDING_PRODUCTION_TIMING_AUDIT`; output remains RESEARCH_ONLY_NOT_CHAMPION until production entry timing is verified. This stage does not force a Traffic corridor.

## 2026-09-07 — SHORT universe first full run exposed benchmark mismatch

First server run scanned 518 symbols / 514 with bars / 1,574,250 bars and reconstructed 7,616 raw base-trigger hits. This is intentionally NOT accepted as the old 1,423 ORIGINAL benchmark: the old stability report therefore included additional eligibility/dedup/cooldown semantics beyond bare `matchConfirmed5m()`, or a different universe contract. The run also exposed a server-timezone bug: CLI date `2026-08-21` became `2026-08-20T23:00:00Z`. Generator date parsing is now explicitly UTC. Next gate is reproduction-contract audit before any SHORT G0 optimization; 7,616 must not be used as profile Traffic.

## 2026-09-07 — Full SHORT base-signal universe generator added

Added SOLO LAB `tools/short_base_signal_universe_v1.php`. It scans DATASET Parser2 symbol `kline5m` histories and mirrors the current Combat `matchConfirmed5m()` base SHORT trigger exactly (24-bar completeness, previous-bar 15m return >=1.20%, deviation from 12-bar mean >=0.80%, previous high breakout, current red reversal below previous midpoint and close). Output is a causal NDJSON signal universe plus coverage report, with no old profile decisions used as labels. Default reconstruction period matches the old stability benchmark, 2026-08-21 through 2026-09-04. This generator is intentionally separate from trajectory/feature mining so signal-universe reproduction can be checked against the prior 1423-allow benchmark before G0 search proceeds.

## 2026-09-07 — SHORT replay reader corrected to actual DATASET Parser2 layout

Audited DATASET Parser2 storage directly. Historical confirmed 5m bars are stored per symbol at `parser2_market_cache/storage/<SYMBOL>/kline5m/YYYY-MM-DD.ndjson` with schema `parser2_native5m_v1` and timestamp field `ts`. Corrected `short_original_g0_causal_replay_v1.php`, which had incorrectly assumed per-day JSON files under the symbol root. The replay now streams confirmed NDJSON bars from the actual DATASET-owned layout. This removes a concrete blocker for SHORT trajectory reconstruction; the remaining blocker is guarded execution/full-universe signal generation, not Parser2 file discovery.

## 2026-09-07 — SHORT full-universe G0 replay contract frozen

Added `SHORT_G0_FULL_REPLAY_SPEC.md` after auditing the surviving SHORT package implementation. The canonical causal feature family is now explicitly tied to the existing handler geometry (`dist_high_1h`, signal body/range/wick, 2h position, 24h range, 15m/2h/4h/6h returns, turnover spike, breakout and previous-candle geometry), with DATASET BTC/ETH, breadth, orderbook, funding/OI and Passport context added only when available as-of. The preserved 56-row pre-entry sample is formally insufficient for G0; the target is reconstruction of the full historical base-signal universe (the prior 15d report had 1423 ORIGINAL allows). Qualification remains +5/+10, independent SHORT STOP grid, chronological holdout, forward total PnL primary and natural Traffic observation. Old E1..E10/S1..S3 rules are candidate atoms only, not privileged profile truth.

## 2026-09-07 — SHORT runner separation corrected at cycle API level

Fixed an incomplete split in `RollingProfileCycleV1`: although SHORT had its own config, `due()` and `window()` were still reading the default LONG config. Both now require `side` and resolve the side-specific config; ORIGINAL/CHALLENGER creation passes the side through, records `runner_id`, and derives profile names from the actual configured training-window hours instead of hard-coded 24H. Challenger promotion metadata was also updated to forward-total-PnL primary with Traffic observation unless explicitly enforced. Workspace search found no current callers of the old `due()` signature, so this change does not leave a known call site broken.

## 2026-09-07 — SHORT runner split into an independent policy/config

SHORT now has its own SOLO LAB runner configuration `rolling_profile_short_v1.json` and runner identity `SHORT_PROFILE_RUNNER_V1`, independent from LONG. It starts with the same safe baseline cadence (1h), 24h training window, +5/+10 PM contract and STOP search grid, but can later self-tune cadence/window independently because SHORT market degradation and useful regimes need not match LONG. LIVE profile following remains separate from LIVE execution enable. Also corrected the shared promotion decision: when Traffic corridor enforcement is disabled, missing corridor thresholds no longer fail closed; forward total PnL is primary and Traffic is recorded as observation. A corridor can still be enforced later by explicitly enabling it.

## 2026-09-07 — SHORT mirrored causal replay tool installed in SOLO LAB

Added `tools/short_original_g0_causal_replay_v1.php` to mirror the LONG G0 trajectory contract for SHORT: +5 floor, +10 native trailing arm, STOP grid -3..-50, 24h MFE/MAE/reach/stop-hit reconstruction from DATASET native 5m bars, natural Traffic observation, and temporal-forward qualification. The tool accepts the preserved legacy pre-entry rows only as causal feature anchors and explicitly warns that this is not the full reconstructed SHORT signal universe, so it cannot by itself freeze G0. It has not been executed because the guarded task allowlist does not expose this new LAB tool.

## 2026-09-07 — Legacy SHORT profile pools cleared before new G0 build

Soft-deleted the old Combat SHORT research/profile pools under DEMO `profile_lab`, `v2_robust/short`, `geo_v2/short`, `phase_geo_v2/short`, and `win_protect/short`. LIVE runtime/package and the base SHORT strategy were deliberately preserved. Historical aggregate/audit reports used as research evidence were also preserved; they are not active profiles. New SHORT generation starts from no Champion in rolling state and must qualify `COMBAT_SHORT_ORIGINAL_G0` under the mirrored LONG causal contract.

## 2026-09-07 — SHORT G0 causal candidate seeds extracted

Created `storage/rolling_profile_v1/short_original_g0_candidate_seed.json`. Three compact SHORT loss-cut seeds were extracted from the existing pre-entry/in-sample audit for later causal temporal replay: (S1) near 1h high + weak signal body, (S2) wide signal + low 2h position, and (S3) tight 24h range. Historical discovery reported 5/3/2 covered losses respectively with zero winner collisions, but these remain research seeds, not G0 proof. The old R7 15d replay is explicitly rejected as qualification evidence because its own baseline validation reports material ORIGINAL reproduction discrepancy. Current PM remains +5 floor/+10 native trailing; STOP grid -3..-50; Traffic remains observation-only.

## 2026-09-07 — SHORT ORIGINAL G0 reset onto the LONG-mirrored research contract

SHORT rolling state no longer pretends that `SHORT_ORIGINAL_24H_G0 / PASS_ALL_BASE_SIGNALS` is a Champion. Champion is now null and status is `BUILD_REQUIRED_NEEDS_CAUSAL_REPLAY`. Added `SHORT_ORIGINAL_G0_RESEARCH_PLAN.md`: same causal build/holdout/runner lifecycle as LONG, fixed +5 floor / +10 native trailing, hard-stop grid -3..-50, natural Traffic observation with no forced corridor, and forward PnL as primary Champion comparison. Existing 15-day ORIGINAL, 6.68l and ORIG10 refinements are retained only as historical/candidate evidence; none is treated as qualified SHORT G0 without replay under the current contract.

## 2026-09-07 — Traffic corridor changed from enforced gate to profile observation

Per runner policy, Traffic is no longer forced into a predefined corridor. `rolling_profile_v1.json` now marks the corridor as `OBSERVE_SELF_DETERMINED_NOT_FORCED` with enforcement disabled. Profiles should carry natural Traffic observations (`observed_traffic_pct`, champion comparison delta, forward PnL and resolved count), allowing the runner to learn later whether a useful corridor exists at all. `COMBAT_LONG_ORIGINAL_G0` now carries an explicit `traffic_observation` marker: observed 49.6350%, holdout 68.8889%, `required_corridor=null`, `corridor_enforced=false`, `WAIT_AND_OBSERVE`. This avoids forcing the market merely to satisfy a Traffic quota.

## 2026-09-07 — G8 causal row reconstruction tool added in SOLO LAB

Added `history_fingerprint_lab/tools/long_g8_causal_rows_v1.php`. It uses historical LONG base-signal anchors, preserves existing as-of BTC/breadth fields, reconstructs symbol depth-band-2 orderbook imbalance from the latest Parser2 snapshot at or before the signal (max age 300s), and reads only subsequent confirmed native 5m bars for labels. It emits row-level MFE/MAE, reach +5/+10, weak-positive-loss marker and STOP hits for -3..-50 ROI. Missing causal fields remain explicit and cannot pass G8. This is the missing row-level substrate for Traffic-corridor and hourly walk-forward replay. The tool has been installed but not yet executed because the guarded task allowlist does not currently expose this new CLI tool; no replay metrics are claimed from it yet.

## 2026-09-07 — SOLO runner promotion gate and Combat AUTO handoff contract wired

`RollingProfileCycleV1` now has an executable promotion decision: challenger must have greater total forward PnL than champion and satisfy both selected relative and absolute Traffic-corridor floors. If corridor thresholds have not yet been empirically selected, promotion fails closed with `TRAFFIC_CORRIDOR_NOT_SELECTED`. The cycle also emits a typed `PUBLISH_AUTO_CHAMPION` handoff contract targeting Combat `LivePromotionControl::publishAutoChampion`, with atomic risk/profile semantics and entry-snapshot ownership. SOLO rolling state was aligned to the frozen LONG champion `COMBAT_LONG_ORIGINAL_G0`; its -40 STOP is explicitly marked as a seed pending exact G8 STOP replay rather than proven optimum.

## 2026-09-07 — LIVE AUTO/MANUAL selector implemented at promotion-control layer

CombatSolo LIVE control now persists `profile_selection_mode` (`AUTO`/`MANUAL`, default MANUAL) independently from LIVE execution ON/OFF. `LivePromotionControl::publishAutoChampion()` accepts runner promotion only in AUTO mode and atomically folds the promoted profile risk snapshot into LIVE settings (`hard_stop_roi_pct`, +5 floor/profit lock, native trailing arm). Manual profile publication now performs the same risk merge. The frozen `COMBAT_LONG_ORIGINAL_G0` artifact is explicitly available/resolvable in LIVE profile control. UI now exposes the AUTO/MANUAL selector. This establishes the safe handoff API for the hourly runner; the runner itself still has to call `publishAutoChampion()` when a challenger is promoted.

## 2026-09-07 — LIVE profile control clarified: AUTO/MANUAL, profile switch carries STOP

Corrected LIVE control semantics. LIVE profile selection is simply `AUTO` or `MANUAL`; LIVE execution enable/disable remains a separate control. In AUTO, LIVE follows the runner champion and a champion switch must atomically carry the profile's risk snapshot, specifically hard STOP plus the fixed +5 floor / +10 native-trailing arm. New entries use the newly selected profile/risk snapshot; already-open positions retain their entry-time ownership/settings snapshot. In MANUAL, the operator chooses the profile and that profile's risk snapshot is applied with the selection. This prevents an AUTO profile change from leaving LIVE on a stale STOP belonging to the previous profile.

## 2026-09-07 — Runner rotation automatic in DEMO/SHADOW; manual control reserved for LIVE

Rolling Profile V1 now requires automatic challenger promotion/rejection in the research/DEMO/SHADOW loop. When the hourly challenger beats the champion on common forward evidence and passes the tested Traffic corridor, it is automatically promoted to DEMO champion and the old champion moves to SHADOW; a failing challenger is automatically rejected. No operator confirmation is required for this loop. Manual confirmation/profile switching is reserved for LIVE only, and research automation must not enable LIVE submit by itself.

## 2026-09-07 — Traffic guard changed from guessed threshold to tested corridor

Runner Traffic protection is now a research parameter rather than a hand-set -10/-15/-20% rule. The config defines a walk-forward grid over relative Traffic loss versus champion (-5/-10/-15/-20/-25/-30%) and absolute minimum Traffic (20/25/30/35/40/45/50%). The corridor is selected by maximum total forward PnL while maintaining stable trade count; selected thresholds remain null until replay evidence exists. Promotion remains PnL-first but is permitted only inside the empirically selected Traffic corridor.

## 2026-09-07 — Runner promotion objective simplified to PnL + Traffic

Hourly challenger promotion is now intentionally simple: compare champion and challenger on common forward/SHADOW evidence; primary metric is total PnL, with Traffic as a mandatory guard so a candidate cannot appear superior merely by collapsing to a tiny number of trades. WR, LOSS rate, expectancy, MFE and MAE remain diagnostics/tiebreakers, not the primary promotion objective. If challenger produces more PnL while preserving useful Traffic, promote it and retain the old champion in SHADOW; otherwise reject the challenger.

## 2026-09-07 — Hourly challenger loop chosen as baseline runner policy

Simplified Rolling Profile V1 operating policy: generate one challenger every hour while the current champion continues working. A challenger is promoted only after it proves better on comparable subsequent SHADOW/forward evidence; on promotion the previous champion is retained in the SHADOW profile library. A challenger that is not better is rejected and is not installed/retained as an active profile. In-sample/training superiority alone cannot trigger replacement. Cadence self-tuning across 1h/3h/6h/12h/24h remains available as a later optimization layer, and degradation-triggered early generation remains optional/off until calibrated.

## 2026-09-07 — Cadence/degradation study protocol started from G0 evidence

Re-ran LONG G8 robustness/regime evidence before cadence work. `COMBAT_LONG_ORIGINAL_G0` remains reproducible at 50W/18L, 73.53% WR, 49.64% traffic; holdout 23W/8L, 74.19% WR, 68.89% traffic. Daily behavior is visibly non-stationary (for example 62.5% WR / 24.24% traffic on 2026-09-01 versus 76.92% / 46.43% on 2026-09-02 and 72.41% / 67.44% on 2026-09-04), supporting adaptive rebuild timing rather than assuming a fixed lifetime. Added `CADENCE_SELF_TUNING_V1.md` with a causal walk-forward comparison of 1h/3h/6h/12h/24h, forward-only scoring, minimum-sample protection, slower-cadence tie preference, and an optional degradation-triggered early rebuild that remains disabled until thresholds are calibrated. Current 1h cadence remains only the seed, not a concluded optimum.

## 2026-09-07 — LONG ORIGINAL G0 frozen in CombatSolo; runner cadence made self-tuning-ready

Froze the robust wide LONG candidate as `COMBAT_LONG_ORIGINAL_G0`: BTC 3h < 0, breadth median 24h <= -0.1028223833%, G6 band-2 orderbook imbalance >= 0.1150402080. Research evidence is 68/137 allowed, 50 WIN / 18 LOSS, 73.53% WR, 49.64% traffic; holdout 74.19% WR / 68.89% traffic. Risk seed is hard STOP -40 ROI with fixed +5 ROI floor and +10 ROI native-trailing arm. CombatSolo DEMO runtime control and LONG package defaults now point to this G0 seed while trading is operator-paused; existing ownership remains snapshot-isolated. A dedicated `long_original_g0.json` control artifact records rules/evidence/runner contract.

SOLO Rolling Profile V1 cadence policy is now explicitly self-tuning-ready. Candidate rebuild intervals are 1h/3h/6h/12h/24h, current seed 1h; `RollingProfileCycleV1::due()` consumes the selected cadence rather than only the legacy minute field. The next cadence study must choose the interval by forward WR/PnL/loss/traffic/stability and can retest after regime change. Degradation-triggered early rebuild is recorded as optional and disabled until statistically defined. PM optimization is disabled: +5 floor / +10 trailing remains fixed while profile/STOP generation adapts.

## 2026-09-07 — ORIGINAL G0 direct all-feature research contract frozen

Defined the clean SOLO LAB research contract for selecting `COMBAT_LONG_ORIGINAL_G0` and `COMBAT_SHORT_ORIGINAL_G0` directly from causal data rather than reusing legacy profile formulas/R2 runner. Candidate training horizons are 6h/12h/24h independently per side. Search may use all causally replayable variables available at decision time: base candle/signal, full 1m/5m/15m geometry, Parser2 G4 market/activity/OI/funding, G5 BTC/ETH multi-horizon phase, G6 orderbook/depth/walls, breadth/time context, and Passport/combat-performance only where a true historical as-of snapshot exists. PM remains fixed (+5 floor, +10 Bybit native trailing); stop search is -3..-50 ROI. Selection must balance +5 reach/WR, LOSS and weak-positive-loss reduction, winner damage, expectancy/PnL, traffic, confidence and temporal robustness. No current Passport values may be leaked into historical rows.


## 2026-09-07 — Legacy CombatSolo DEMO/SHADOW runtime reset for Rolling G0

At operator request, soft-deleted the old LONG and SHORT CombatSolo DEMO runtime trees and legacy SHADOW/profile-pool controls (`long_shadow_profiles.json`, `short_dynamic_profiles.json`, stale SHORT profile switch control). LIVE runtime, LIVE publication/control, packages and LIVE simulation were intentionally preserved. The new SOLO Rolling Profile V1 `LONG_ORIGINAL_24H_G0` / `SHORT_ORIGINAL_24H_G0` artifacts remain the only new-generation profile baselines; they are LAB baselines pending causal 24h replay and are not falsely marked as installed CombatSolo DEMO profiles.


## 2026-09-07 — Rolling profile generation reset to G0 ORIGINAL

Reset the new SOLO Rolling Profile V1 generation state only (legacy research artifacts and CombatSolo DEMO/LIVE state were not deleted). Created independent `LONG_ORIGINAL_24H_G0` and `SHORT_ORIGINAL_24H_G0` baselines with PASS_ALL_BASE_SIGNALS, fixed PM lifecycle (+5 ROI floor, +10 ROI Bybit native trailing), MFE/MAE tracking contract, and hard-stop search range -3..-50 ROI. No stop winner or WR/PnL metrics were fabricated: both ORIGINAL baselines are explicitly marked `BASELINE_CREATED_REPLAY_REQUIRED` until the causal trailing-24h replay is executed.


## 2026-09-07 — Rolling STOP search extended to -50; PM modeled as floor + trailing

Corrected the Rolling Profile V1 risk/reward research contract. Hard-stop search now spans -3 through -50 ROI. PM is no longer modeled as a take-profit target grid: research policy is +5 ROI profit floor/lock followed by Bybit-native trailing from +10 ROI. The 24h builder must replay that lifecycle while optimizing entry filters and stop depth; sub-+5 outcomes are not target WINs. No CombatSolo runtime settings were changed by this LAB configuration update.


## 2026-09-07 — Rolling profile builder contract expanded to STOP + PM search

SOLO Rolling Profile V1 now treats risk/reward as part of each generated profile, not a fixed external constant. Every causal 24h build is configured to jointly test hard-stop ROI candidates (-3 through -15 initial grid) and PM ROI candidates starting at +5. PM below +5 ROI is excluded from WIN optimization by current research policy. Tight stops are not assumed beneficial: builder must measure whether they convert eventual winners into losses. Same-bar STOP+PM is conservatively LOSS. The selected STOP/PM pair becomes part of challenger quality and must still preserve useful traffic and improve measured SHADOW/out-of-sample economics.


## 2026-09-07 — SOLO rolling 24h profile generation contract started

Added SOLO LAB `history_fingerprint_lab` Rolling Profile V1 configuration and cycle descriptor. A fresh generation starts from ORIGINAL trained on the preceding 24 causal hours; LONG/SHORT are independent. Challenger rebuild cadence is configurable via `rebuild_interval_minutes` and starts at 60 minutes. Newer candidates do not replace the champion merely by age: promotion requires better SHADOW/out-of-sample evidence. Candidate search explicitly targets loss reduction with zero WIN/Traffic damage as a hypothesis/search constraint, not as an assumed achievable fact. No LIVE/DEMO profile was switched by this change and automatic LIVE activation remains disabled.


## 2026-09-07 — Symbol Ban demoted to operator second-line control

Separated fundamental coin quality from the legacy hard-stop ban mechanism. New hard-stop observations no longer create `ACTIVE_AUTO` blocks even when the legacy config says AUTO; they remain `OBSERVED` for review/statistics. `globalBlockedMap()` now propagates only explicit operator bans (`APPROVE` temporary or `PERMANENT` permanent). Coin Passport `combat_performance` is the automatic/fundamental entry-quality layer; Symbol Ban is retained as a manual emergency/exception layer. Existing explicit operator permanent bans remain intact and were not silently cleared.


## 2026-09-07 — Coin Passport combat-performance entry filter enabled for LONG + SHORT DEMO

Added operator setting `use_combat_performance` to the standard strategy Settings UI and wired `CombatPerformanceFilter` into both LONG and SHORT new-entry scans. The filter reads DATASET-owned Coin Passport shadow runtime and applies only directional exclusions: `AVOID_BOTH` blocks LONG+SHORT, `LONG_PREFERRED` blocks SHORT, `SHORT_PREFERRED` blocks LONG; `BOTH_GOOD`, `MIXED_INSUFFICIENT`, UNKNOWN or missing Passport data fail open and do not block. The setting is now ON for both current DEMO strategies. Existing/open positions are untouched. LIVE mirror behavior is not independently changed; it can only mirror DEMO sources that survive the DEMO entry chain.


## 2026-09-07 — CombatSolo → DATASET Coin Passport feedback wired end-to-end

Added authenticated `POST /api/v1/live_engine/combat_performance.php` on DATASET for the approved `combatsolo.info` live-engine identity. CombatSolo now aggregates deduplicated CLOSED DEMO+LIVE outcomes per symbol, splits them into ALL/LONG/SHORT, attaches aggregate hard-stop counts, and publishes the batch through its existing DATASET engine token during engine ticks. DATASET validates/normalizes each symbol and stores feedback only inside DATASET-owned Coin Passport ingestion storage; the Passport runtime publisher projects it as analysis-only `combat_performance`. Feedback transport failure is nonfatal to the trading tick and cannot grant trade permission. No profile or LIVE/DEMO activation state was changed.


## 2026-09-07 — Combat-performance no-edge boundary set to 50% WR

Adjusted the initial Coin Passport combat-performance classification: 50% WR is treated as the no-edge / lottery boundary rather than acceptable symbol quality. `AVOID_BOTH` now requires at least 15 resolved ALL trades, WR <50%, and negative cumulative closed PnL. Preferred directional classification remains stricter at WR >=60% plus positive PnL. This changes Passport feature classification only; no LIVE/DEMO execution or profile activation state was changed.


## 2026-09-07 — Coin Passport combat-performance feature contract

Added DATASET-owned `coin_passport_combat_performance_v1` projection for feedback produced by CombatSolo. The normalized Passport runtime can now expose ALL/LONG/SHORT wins, losses, resolved sample, WR, cumulative closed PnL, hard-stop count, confidence and descriptive `direction_fit` (`LONG_PREFERRED`, `SHORT_PREFERRED`, `BOTH_GOOD`, `AVOID_BOTH`, `MIXED_INSUFFICIENT`). The projection is explicitly analysis-only and cannot authorize trades. DATASET reads only its own feedback-ingestion area; it does not reach into CombatSolo runtime storage. Initial conservative classification defaults require sample support plus WR/PnL agreement and are documented as research defaults to validate later in SOLO LAB. Producer transport from CombatSolo into DATASET ingestion remains the next integration step; no execution/profile state changed.


## 2026-09-07 — Toxic-symbol review now uses full WIN/LOSS context

Corrected the global Symbol Ban policy so `3+ HARD STOP` is a review threshold, not sufficient evidence for an automatic global block by itself. The ban layer now reconstructs deduplicated closed-trade statistics per symbol across LONG+SHORT and DEMO+LIVE ownership/trade records and exposes WIN, LOSS, WR and cumulative closed PnL next to hard-stop count. This prevents pathological decisions such as globally banning a symbol with 100 wins and only 3 losses. Symbols with 3+ hard stops are labelled `REVIEW 3+` unless an actual active AUTO/manual/permanent ban exists. Existing active bans still propagate globally. No existing ban records were deleted and no execution state was changed.


## 2026-09-07 — LIVE control split into operator tabs

Reorganized `/strategy-live-control` into four focused tabs while preserving the existing guarded actions and state model: `Профиль` contains DEMO/LIVE summary cards, pending SHADOW approval and immutable LIVE profile publication; `API ключ + баланс` contains LIVE key binding/read-only test plus account balance; `Execution` contains readiness gates and the explicit operator LIVE ON/OFF switch; `Диагностика` contains the full network-order-free LIVE dry-run. This is a presentation split only: promotion revisions, key ownership, readiness checks and execution semantics are unchanged. The selected strategy remains explicit in every URL/action and no execution state was changed by the UI refactor.


## 2026-09-07 — Strategy detail UI split: Settings / Profiles / Symbol Ban

Separated overloaded strategy Settings into dedicated operator tabs. Strategy detail now exposes `Обзор`, `Настройки`, `Профили`, `Бан монет`, `Позиции`, `История` while preserving DEMO/LIVE context. SHORT Profile Lab moved out of Settings into `Профили`; LONG receives the same Profiles tab with a direct path to its LONG SHADOW profile table. Shared LONG+SHORT toxic-symbol controls and tables moved into `Бан монет`, including global stop aggregation, manual ban, permanent ban, dismiss and clear actions. Symbol-ban POST actions now return to the Ban tab instead of Settings. Separately, investigation of SHORT LIVE `DEGRADED` confirmed the failed OPUSDT submit was not a zero-balance rejection: Bybit returned a regulatory product/service restriction. The runner records this as an OPEN error, leaves mirror state unopened, continues the scan, and reports DEGRADED rather than crashing. No retry mirror row is created for a failed order. LIVE remains operator-enabled as previously configured; this UI pass did not change execution state.


## 2026-09-07 — Strategy Overview metrics isolated by selected environment

Finished the critical metric-isolation part of the DEMO/LIVE detail-page split. Rich Overview calculations (realized/unrealized PnL, equity curve, daily PnL, drawdown, profit factor, average ROI/hold/leverage, wins/losses/trades and current positions) now filter persisted ownership/trades to the selected `env=DEMO|LIVE` before aggregation instead of calculating over the combined environments. The rich current-position and closed-24h tables likewise use only the selected environment. Overview labels now identify the selected environment rather than saying DEMO unconditionally, and the open-position KPI no longer embeds mixed DEMO/LIVE counts. Navigation for Settings/Positions/History preserves the environment parameter. Manual close controls remain available only in DEMO; LIVE rows are read-only here and direct the operator to LIVE control. No execution state was changed.


## 2026-09-07 — DEMO/LIVE strategy symmetry completed further + global toxic-symbol threshold

Continued the four-view strategy UI pass. Strategy detail now preserves explicit DEMO/LIVE context across Settings, Positions and History; Positions and History no longer merge both environments when an environment tab is selected. Settings use the same standard form/layout in both contexts, while LIVE explicitly states that editing working runtime settings is not itself a LIVE publication: immutable LIVE profile/revision/key/execution confirmation remains under LIVE control. Symbol Ban was promoted from a per-strategy display to a shared toxic-symbol layer: the UI aggregates HARD STOP counts across LONG and SHORT, shows per-side contribution, and marks the global state. Both LONG and SHORT DEMO scanners now consult the same global blocked map. A symbol is blocked for new entries across both sides when its aggregate recorded HARD STOP count reaches 3; an existing active/permanent strategy ban also propagates into the shared blocked map. This deliberately prevents profile switching from re-admitting a repeatedly toxic symbol. Existing stop records are reused; no synthetic losses were created. LIVE execution remains disabled and was not enabled by this change.


## 2026-09-07 — Strategies UI: four environment/side tabs + shared symbol-ban scope

Reworked `/strategies` so DEMO and LIVE statistics are no longer mixed inside the same strategy card. The page now has four standard views: `DEMO · LONG`, `DEMO · SHORT`, `LIVE · LONG`, `LIVE · SHORT`. Each card and aggregate summary use only the selected environment and side; opening a card carries an explicit `env=demo|live` context into the strategy page. The strategy page now exposes DEMO/LIVE context tabs and filters positions/recent events to the selected environment while retaining the familiar Overview/Settings/Positions/History layout. This is the first UI pass toward symmetric DEMO/LIVE strategy pages; LIVE promotion/key/execution safety remains in the dedicated LIVE control. Symbol-ban configuration metadata was changed from DEMO-only to strategy-shared DEMO+LIVE scope because repeated toxic symbols are a market/symbol property relevant to both environments. Existing per-strategy ban entries were preserved; no bans were added or removed and no LIVE execution was enabled.


## 2026-09-07 — LIVE control UI split into DEMO and LIVE cards

Reworked the top of LIVE control around the operator's actual mental model instead of a numbered setup sequence. The page now starts with two side-by-side summary cards: DEMO shows the currently active DEMO profile and links directly to SHADOW statistics/settings; LIVE separately shows the currently published immutable LIVE profile, revision, operator ON/OFF and effective submit READY/BLOCKED state. A compact flow line makes the separation explicit: SHADOW → DEMO for immediate testing, or SHADOW → LIVE PENDING → operator confirmation → separate LIVE ON. Pending promotion remains directly below these cards. Lower controls are now labelled by purpose (`LIVE профиль · публикация`, `LIVE ключ`, `LIVE execution · запуск`) rather than ambiguous numbered steps. No runtime/profile/key/trading state was changed by this UI-only pass.


## 2026-09-07 — SHADOW row actions: DEMO now / LIVE pending

Added profile promotion actions directly beside the statistics in both LONG and SHORT SHADOW tables, so the operator can choose based on the visible WR/PnL/Traffic/sample evidence instead of a stat-less dropdown. Execution-compatible rows now expose separate DEMO and LIVE actions. LONG `→ DEMO` applies `long_entry_profile` immediately for new DEMO entries while preserving ownership of already-open positions. SHORT DEMO keeps its existing safe runner-bound switch/cooldown path rather than bypassing its established atomic switch contract. `→ LIVE pending` does not publish or activate the profile: it stores a pending immutable candidate in `LivePromotionControl` with profile/package/settings hashes. LIVE control displays the pending candidate and requires an explicit operator confirmation to publish it; LIVE execution still requires the separate LIVE ON action and all safety gates. FAT delayed-entry observers remain SHADOW-only and therefore have no DEMO/LIVE buttons. Existing direct LIVE profile selector remains as an additional operator path. No LIVE execution was enabled.


## 2026-09-07 — LIVE key selector ownership fix / SHORT binding verified

Investigated `key_already_bound_to_strategy_instance:exhaustion_short_120m_v1@LIVE`. The key was not lost: `strategy_key_bindings.json` already contains the LIVE binding for SHORT (`its live`, mainnet). The LIVE-control page was incorrectly listing every LIVE key rather than only keys selectable for the current strategy, so the LONG page could offer a key already owned by SHORT and then fail correctly in the binding layer. LIVE control now uses `StrategyKeyBinding::selectableKeys(strategy, LIVE)` and therefore hides keys owned by another strategy while retaining the current strategy's own key. Rebinding the same key to the same strategy is treated idempotently as already bound rather than an error. No key ownership was moved and LIVE execution was not enabled.


## 2026-09-07 — Unified manual DEMO/LIVE profile selection for LONG + SHORT

Aligned LONG and SHORT around the same operator model: one manually selected execution profile for DEMO, while the rest continue as research/SHADOW; LIVE has an independent manually published immutable profile snapshot. LONG now declares `long_entry_profile` with default `ORIGINAL_LONG_F4_R50_B2`. Execution-compatible LONG filter profiles (structured-rule profiles; FAT delayed-entry observers are explicitly excluded) can be selected from the normal strategy Settings page. On each confirmed base LONG signal the runner evaluates the selected profile and opens a DEMO position only on `ALLOW`; DATA_MISSING/BLOCK/OUT_OF_REGIME are skipped and journaled. The selected profile ID is persisted into ownership. SHORT retains its existing safe queued DEMO profile-switch mechanism. LIVE control for both LONG and SHORT now exposes a profile selector that publishes the chosen profile as an immutable LIVE revision without changing DEMO and without enabling LIVE execution. The existing “publish current DEMO” action remains available. Profile publication and LIVE ON/OFF remain separate safety actions; no LIVE execution was enabled by this change. FAT G13/G18 remain SHADOW_ONLY and are intentionally not offered as execution profiles because their delayed confirmation/next-open semantics are not the same as the normal entry-filter execution path.


## 2026-09-07 — FAT/G18 repeat-signal lifecycle fixed

Completed the LONG FAT observer lifecycle audit. FAT trades were keyed only by `profile -> symbol`; after the first CLOSED/UNRESOLVED trade, `isset(trades[profile][symbol])` permanently prevented that symbol from becoming a candidate again. Changed candidate admission so only an existing OPEN trade blocks a repeat signal. Before a later trade replaces a completed per-symbol slot, the completed record is appended to `trade_history` with its symbol, preserving prior forward evidence. FAT statistics now aggregate archived history plus the current per-symbol trade slot, and explicitly count `UNRESOLVED` outcomes separately. This allows repeated independent G13/G18 samples on the same symbol without losing earlier W/L/ROI history. SHADOW_ONLY authority is unchanged; no real LONG entry rule, position, or order path was modified.


## 2026-09-07 — LONG new-profile signal outage: missing BTC3h helper fixed

Investigated the apparent absence of signals/data on newly added LONG SHADOW profiles. Runtime history contained repeated `DEMO_RUNNER_ERROR: Call to undefined method DatasetAsOfFeatureReaderV1::btcReturn3h()` events. `collectG4BtcPhase()` referenced a helper that was never implemented, so any confirmed LONG signal reaching the new G4/G5/G6/G18 feature path could throw before SHADOW evaluation and before normal entry processing completed. Replaced the invalid helper call with the same causal native BTCUSDT 5m as-of calculation already used by `collectR2`: close at decision timestamp versus close 3h earlier. This restores `research_btc_ret_3h_pct` without future data and without changing profile thresholds or trading authority. The earlier FAT observer pre-gate tap remains in place. No historical samples are fabricated; new SHADOW samples accumulate only from future confirmed signals after the fix.


## 2026-09-07 — LONG FAT/G18 SHADOW signal-tap independence fix

Investigated why `LONG_G18_WIN90_SHADOW` remained at zero for several hours. The FAT observer feature tap was located after real-entry eligibility gates (`reserved`, same-symbol cooldown, symbol ban and related early continues), so a valid base LONG signal could disappear before G18/G13 received its research feature vector. Moved/duplicated the FAT observer feature collection immediately after the confirmed base LONG signal match and before those real-entry gates. FAT observers now receive every confirmed base LONG signal independently of whether the production/demo LONG path can open that symbol. The observer remains SHADOW_ONLY: it cannot block, open, close or modify a real position. Existing real-entry gates and order path are unchanged. This fixes forward-sample wiring; it does not loosen G18 rules or retroactively fabricate samples.


## 2026-09-07 — SHORT ACTIVE source-of-truth / control-desync UI

Audited the SHORT ACTIVE discrepancy. `strategy_runtime_control.json` currently has `short_entry_profile=ORIGINAL`, while `short_profile_switch_control.json` is a stale switch journal with `status=ACTIVE, active_profile=SHORT_V2_R6`. Historical runtime events confirm SHORT_V2_R6 was activated earlier, while later entries/runtime returned to ORIGINAL. No profile was switched during this audit. `strategy-shadow.php` now treats strategy runtime control as the authoritative current ACTIVE source and uses the switch file only for pending REQUESTED/QUEUED state and switch history. If a stale ACTIVE switch journal disagrees with runtime, the UI explicitly renders `CONTROL DESYNC`, shows both values, and labels the main ACTIVE card `ACTIVE · runtime`. This is diagnostic/UI-only and does not alter trading state.


## 2026-09-07 — LONG/SHORT runtime + SHADOW UI audit

Ran the guarded cross-system self-test after G18 SHADOW installation: PASS, no syntax failures, registry/mapping/cross safety OK, LONG contract/stage/keys/ownership safe, SHORT exhaustion/shadow/rescue-shadow safe, `live_submit_enabled=false`, 42 managed LONG owners, and no self-test side effects. Reviewed both SHADOW UI implementations. SHORT correctly renders non-selectable profiles as disabled `SHADOW only` while preserving manual switching only for selectable profiles. LONG registry rendering includes all profiles including zero-forward observers. Fixed a LONG UI observability gap for FAT/G18 observers by exposing virtual ROI sum/average and candidates waiting; table empty-state colspan updated accordingly. No trading behavior changed by the UI patch.


## 2026-09-07 — G18 installed into forward SHADOW

Installed `LONG_G18_WIN90_SHADOW` as non-selectable `FAT_ENTRY_OBSERVER`, `blocks_real_entry=false`, for unseen forward validation of the historical 91.89% candidate. Candidate gates preserve the G10 FAT selector and add `g4_open_interest_delta_pct >= -0.27530677` plus `g5_btc_ret_1h_pct >= -0.33966960`; confirmation uses `entry_body_pct >= 1.50` and close-from-signal ROI >= -4. The FAT forward scorer was corrected from the known same-confirmation-bar-open leak to `confirmation bar CLOSE -> ENTRY_ARMED -> next confirmed 5m OPEN`. Forward outcome now exactly matches research: +10 ROI first = WIN, -3 ROI first = LOSS, same 5m bar both = LOSS, unresolved at 24h stays unresolved. G5 macro features are now collected read-only for SHADOW evaluation. Historical 91.89% remains explicitly research/theory, not expected live WR. No Active/DEMO/LIVE order behavior changed.


## 2026-09-07 — G18 88–90% WR frontier

Added guarded analysis-only two-condition residual-LOSS search on top of the G16 `body_pct >= 1.50` entry and the binary +10/-3 first-touch outcome. Tested 749 viable pairs; 173 passed robustness/traffic gates and 53 reached >=88% full-sample WR. Best mathematical pair: `g4_open_interest_delta_pct >= -0.27530677` AND `g5_btc_ret_1h_pct >= -0.33966960`: 37 resolved, 34W/3L = 91.89% WR, +331 ROI points, +8.95 ROI/trade, 69.81% G16 coverage; train 90.91%, holdout 93.33%. A better traffic/complexity compromise is `g4_spread_pct >= 0.01750777` AND `g5_btc_ret_1h_pct >= -0.33966960`: 44 resolved, 39W/5L = 88.64% WR, +375 ROI points, +8.52 ROI/trade, 83.02% coverage; train 85.71%, holdout 93.75%. Another simple entry-shape pair `g4_spread_pct >= 0.01750777` AND `entry_higher_low >= 1` gives 43 resolved, 38W/5L = 88.37%, 81.13% coverage. Research frontier only; no Active/DEMO/LIVE change.


## 2026-09-06 — G17 residual LOSS signature / 85% frontier

Fixed a PHP expression in the G17 loss-signature analyzer that caused the initial guarded runner failure, then completed the analysis. Base is G16 body>=1.50 under +10/-3 first-touch: 53 resolved, 42W/11L, WR79.25%, +387 ROI points. A portable BTC3h condition `research_btc_ret_3h_pct >= 0.2694212646` retains 40/53 entries (75.47% coverage) and yields 34W/6L = 85.0% WR, +322 ROI points, +8.05 ROI/trade; train 78.57%, holdout 12W/0L = 100%. A broader BTC24h condition >= -0.63765248 retains 45/53 (84.91%) at 84.44% WR and +7.98 ROI/trade. A very broad `entry_higher_low >= 1` retains 48/53 (90.57%) at 83.33% WR and +7.83 ROI/trade. These are research frontiers, not forward validation; no Active/DEMO/LIVE change.


## 2026-09-06 — G16 +10/-3 WIN-rate lift

Swept neighboring causal entry confirmation thresholds while keeping the G15 binary contract (+10 ROI WIN, -3 ROI LOSS, same 5m bar both = LOSS). 63 combinations qualified. Best current cut is `body_pct >= 1.75%` with `close_from_signal_roi >= -4` (the close floor is non-binding across -4..-2 in this sample): 47 resolved entries, 38W/9L, WR 80.85%, +353 ROI points, +7.51 ROI/trade, 37.30% coverage of 126 candidates. Train 24W/8L = 75.0%, holdout 14W/1L = 93.33%. No bad evaluated day; daily WR 72.73%, 100% (n=1), 50% (n=4), 92.86%, 100%. A less selective 1.50% body cut gives 53 entries, 42W/11L = 79.25%, +7.30 ROI/trade and 42.06% coverage. Research only; no Active/DEMO/LIVE change.


## 2026-09-06 — G15 binary +10 WIN / -3 LOSS validation

Added guarded analysis-only `long_profile_g15_win10_loss3` using the robust G13 causal entry. Outcome is deliberately simple: first touch +10 ROI = WIN credited +10; first touch -3 ROI = LOSS charged -3; if both levels occur in the same 5m candle, LOSS is counted conservatively. On 70 resolved entries: 48W/22L, WR 68.57%, aggregate +414 ROI points, +5.91 ROI/trade. Train 30W/19L, WR 61.22%, +4.96/trade; holdout 18W/3L, WR 85.71%, +8.14/trade. Every observed day is positive under this binary contract. Breakeven WR is 23.08%. 15 losses were conservative same-bar-both cases, so no favorable intrabar ordering was assumed. Research only; Active/DEMO/LIVE unchanged.


## 2026-09-06 — G13 FAT forward delayed-entry SHADOW scorer

Added a stateful FAT SHADOW observer to the LONG runner. It records G13 candidates, waits up to 60 minutes for the causal entry trigger, opens a virtual FAT trade only, applies the research -3 ROI stop, and closes the virtual observation at stop or 24h. It writes `fat_shadow_state.json` and `fat_shadow_stats.json`; it sends no exchange order and cannot delay/block ORIGINAL LONG. LONG SHADOW UI now reads the dedicated FAT stats for FAT observer rows rather than treating ordinary base-signal profile stats as FAT results. Installed G13 contract remains body >= ~1.05756% and close-from-signal >= -4 ROI, supported by neighboring-threshold robustness.


## 2026-09-06 — root documentation introduced

Domain: WWW root.

Change: created `/docs` as the common project revision/documentation entrypoint with domain map, current state and chronological revision log.

Why: project knowledge had become distributed across chat and domain-specific files, making later revision expensive and ambiguous.

Policy: material changes are documented during the same work session.

## 2026-09-06 — profile-generation architecture clarified

Domains: CombatSolo / SOLO / DATASET.

Decision: do not search for one permanent ideal LONG/SHORT formula. Maintain multiple qualified SHADOW profiles for different recurring market regimes and eventually select the current champion using rolling evidence.

Evaluation dimensions: WR, Traffic, LOSS capture/reduction, WIN damage, expectancy/PnL where available, sample confidence, temporal holdout, causal replay and forward behavior. A ~65% WR level is a practical reference, not a hard universal law.

Modern feature direction: geometry, Research/Passport-equivalent features, market/breadth, BTC/ETH context, orderbook and phase/regime information are feature sources from which compact profiles are generated. Profile generation should use causal data and temporal holdout to reduce overfit.

## 2026-09-06 — profile generations separated

Domain: CombatSolo.

Change: legacy/base-feature profiles were logically separated from newer RESEARCH_V2, GEO_V2 and PHASE_GEO_V2 generations. Legacy definitions were not physically deleted because runtime/history/Active/SHADOW references can still depend on them.

Important forward observation: `LONG_PHASE_GEO_V2_R1` is modern by feature generation but was dead/overfiltered in observed forward statistics (0 Traffic on the inspected resolved sample). Modern generation does not automatically mean good current profile.

## 2026-09-06 — DATASET feature inventory

Domains: DATASET / CombatSolo.

Change: DATASET was explicitly treated as the central provider rather than reconstructing source state independently inside CombatSolo. Parser2 native5m, market history, orderbook, Coin Passport, Wave/regime and related stores were inventoried for profile research.

Important limitation: Coin Passport full historical retention observed was short relative to desired long replay. Long historical research therefore needs Parser2 history plus deterministic causal reconstruction where possible; unavailable historical fields must remain missing/forward-only.

## 2026-09-06 — R2 causal as-of reader and historical Traffic replay

Domains: CombatSolo / DATASET / SOLO.

Created research components:
- `combatsolo.info/engine/strategies/dataset_asof_feature_reader_v1.php`
- `combatsolo.info/engine/strategies/long_winprotect_r2_history_replay.php`

R2 replay uses five unchanged rules involving UTC hour, market breadth median 24h return, 0.25% orderbook bid/ask notional ratio, BTC 3h return and 15m geometry range contraction.

Purpose is Traffic/coverage sanity, not profitability validation.

## 2026-09-06 — SOLO guarded R2 task

Domains: SOLO / CombatSolo.

Change: added dedicated guarded task name `long_winprotect_r2_history_replay` to the SOLO research execution path, mapped to a fixed CombatSolo research function rather than arbitrary command execution.

First invocation reached the new task but failed on an unnecessary LONG handler include. The replay already had its own base matcher, so the unused include was removed. Guarded self-test after this fix: PASS; LIVE submit false; side effects NONE.

Second invocation executed far enough to hit nginx 504. The expected historical report did not exist afterward. No historical Traffic result is therefore claimed.

Next action: optimize/stage replay (one-pass base signal discovery and cached/precomputed historical feature lookup) so the research task completes inside operational time limits. Do not repeatedly run the same expensive implementation.

## 2026-09-06 — R2 replay converted to staged execution

Domains: SOLO / CombatSolo / DATASET.

Change: the guarded R2 replay now advances one UTC day per invocation and persists progress in `combatsolo.info/storage/live_01/runtime/long_winprotect_r2_history_traffic_state.json`. Historical market/orderbook reads gained in-process caches, and orderbook lookup was fixed to scan every relevant hour in the causal max-age window rather than only the target hour.

Performance rule: when required historical orderbook is absent, the vector is already undecidable; expensive GEO/breadth work is skipped for that signal instead of wasting a full historical scan. No value is fabricated.

Result: days 2026-08-21 through 2026-08-29 completed and were checkpointed. All signals in those completed days were undecidable because the required historical 0.25% orderbook feature was unavailable. Example base-signal counts: Aug21=63, Aug22=1446, Aug23=88, Aug24=150, Aug25=134, Aug26=88, Aug27=17, Aug28=164, Aug29=12. These are coverage/base-signal diagnostics, not R2 Traffic results.

The next stage, 2026-08-30, still exceeded the synchronous runner/gateway limit once orderbook became available and expensive GEO/breadth evaluation started. Therefore the replay is not complete and no aggregate historical R2 Traffic is claimed yet. Next optimization must stage the expensive post-orderbook work more finely (signal/symbol batches and/or precomputed breadth) rather than restarting completed days.

Safety after code changes: guarded self-test PASS; LIVE submit false; side effects NONE; R2 remains SHADOW.

## 2026-09-06 — R2 replay symbol batching

Domains: SOLO / CombatSolo.

Change: post-orderbook replay was further split by symbol batches while retaining the full 515-symbol universe for breadth calculation. This avoids corrupting breadth by calculating it only from the current batch. The checkpoint now tracks `symbol_offset` inside the current day, so failed/slow batches do not restart the day.

Verified progress: 2026-08-30 advanced from symbol offset 0 to 60. The first 10-symbol batch contained no base signals. The next 50-symbol batch contained 2 base LONG signals; both were undecidable because the required historical 0.25% orderbook ratio was missing at those signal timestamps. No ALLOW/BLOCK Traffic inference was made for those signals.

Performance finding: the following batch still exceeded the synchronous execution window even when reduced back to 10 symbols. This isolates the remaining bottleneck to one or more signals for which orderbook exists and the replay proceeds into expensive GEO/full-universe breadth calculation. Next optimization is rule-aware short-circuit evaluation plus precomputed/cached breadth by unique signal timestamp; full breadth must never be replaced by batch-only breadth.

No final R2 historical Traffic report exists yet; do not treat partial coverage as profile performance.

## 2026-09-06 — R2 rule-aware short-circuit added

Domains: CombatSolo / SOLO.

Change: the causal R2 reader now evaluates cheap OR-blocking rules (UTC hour, historical orderbook ratio, BTC 3h return) before expensive GEO and full-universe breadth. If a cheap rule already proves BLOCK, the replay may skip GEO/breadth and still return a complete BLOCK decision. If required historical orderbook is missing, the vector remains undecidable. This preserves formula semantics while avoiding unnecessary work.

Validation: guarded self-test PASS; syntax failures none; LIVE submit false; side effects NONE.

Execution finding: the current batch beginning at symbol offset 60 still exceeded the synchronous runner window and did not advance the checkpoint. Therefore the expensive path contains signals not resolved by cheap rules. Next step is persistent breadth precomputation/cache by unique decision timestamp (or a dedicated breadth stage), followed by GEO only where still necessary. Existing completed checkpoints remain intact.

## 2026-09-06 — persistent R2 breadth cache / evaluation order

Domains: CombatSolo / DATASET / SOLO.

Change: historical breadth results are now designed to persist under `combatsolo.info/storage/live_01/runtime/research_cache/r2_breadth/<decision_ts>.json`, keyed by causal decision timestamp. Breadth was moved ahead of GEO for signals not already resolved by cheap UTC/orderbook/BTC rules; if breadth itself proves the OR BLOCK, GEO is skipped. This preserves the exact R2 OR semantics while reducing repeated geometry work.

Validation after changes: guarded self-test PASS, syntax failures none, LIVE submit false, side effects NONE.

First synchronous attempt still timed out before producing a breadth cache file, showing that computing even the first full-universe breadth timestamp through the existing per-symbol historical reader exceeds the gateway budget. Therefore the remaining bottleneck is now specifically full-universe breadth construction, not generic replay dispatch. Next implementation should build breadth incrementally/persistently by universe chunks for one timestamp, then finalize its median and resume the signal replay.

## 2026-09-06 — incremental breadth checkpoint works

Domains: SOLO / CombatSolo / DATASET.

Change: breadth construction is now resumable in 25-symbol chunks per causal decision timestamp. Partial returns are persisted as `research_cache/r2_breadth/<decision_ts>.partial.json`; once all 515 canonical symbols are scanned, the median is finalized into `<decision_ts>.json` and the partial file is removed. Replay returns `BREADTH_PARTIAL` without advancing the strategy symbol checkpoint, so the same signal resumes safely and no partial strategy counts are double-counted.

Verified live research progress: the previously blocking signal is `AUCTIONUSDT`, signal timestamp `1788123900`, on 2026-08-30 at strategy symbol offset 60. Three guarded invocations completed breadth chunks successfully: 25/515, then 50/515, then 75/515. No gateway timeout occurred in these chunked calls.

Safety validation before execution: self-test PASS; syntax failures none; LIVE submit false; side effects NONE. This confirms the incremental design solves the immediate synchronous timeout mechanism while preserving causal full-universe breadth semantics.

## 2026-09-06 — first expensive R2 signal completed

The incremental breadth for the blocking `AUCTIONUSDT` signal was advanced from 75 through the full 515-symbol canonical universe without gateway timeout. After breadth finalization, the replay resumed normally and advanced the 2026-08-30 strategy checkpoint from symbol offset 60 to 70.

Batch result for symbols 60–69: 3 base LONG signals, 1 fully decidable and 2 undecidable due missing historical 0.25% orderbook ratio. The single decidable signal was ALLOW, so this batch has Traffic 100% among decidable signals but only 33.33% versus all three base signals. This is a tiny partial batch and must not be interpreted as aggregate R2 performance.

Important verification: the completed breadth path demonstrates that persistent 25-symbol chunks can finish a full 515-symbol historical market breadth timestamp and then return control to the strategy replay correctly. Continue from offset 70; do not reset completed breadth or earlier date checkpoints.

## 2026-09-06 — R2 replay advanced to offset 110

The 2026-08-30 strategy scan advanced cleanly through offsets 70, 80, 90, 100 and 110. Batches 70–109 contained no base LONG signals, so they required no expensive feature replay.

At offset 110 a new expensive signal was reached: `BTRUSDT`, signal timestamp `1788081600`. Its full-universe breadth checkpoint is now progressing normally in 25-symbol chunks and has reached 100/515. Strategy offset intentionally remains 110 until this causal breadth timestamp is finalized; this prevents partial/double counting.

No 504 occurred during these incremental breadth calls. Final historical R2 Traffic remains pending.

## 2026-09-06 — BTR breadth finalized; replay at offset 130

The `BTRUSDT` breadth timestamp `1788081600` completed the full canonical universe scan. The finalized breadth had 511 usable market returns out of 515 symbols. Replay then advanced from offset 110 to 120 and subsequently to 130 without timeout.

The 110–119 batch contained 2 base signals, both undecidable: one lacked historical BTC 3h return despite completed breadth (511/515), and the second lacked BTC 3h return plus historical orderbook ratio. No ALLOW/BLOCK decision was counted for these two. The 120–129 batch contained no base LONG signals.

Current 2026-08-30 strategy checkpoint: 130/515. Historical R2 aggregate report remains incomplete; partial batches are coverage diagnostics only.

## 2026-09-06 — R2 replay advanced to offset 200

The 2026-08-30 scan advanced from 130 through 200/515 without timeout. Batches 130–169 and 180–199 contained no base LONG signals. Batch 170–179 contained 3 base signals: 1 was decisively BLOCKED by R2 and 2 were undecidable because both historical BTC 3h return and orderbook ratio were unavailable. Thus this batch had 0% Traffic among its single decidable signal, but the sample is far too small for profile conclusions.

Current checkpoint is 200/515 for 2026-08-30. No final aggregate Traffic claim yet.

## 2026-09-06 — R2 replay advanced to offset 230

The 2026-08-30 scan advanced from 200 to 230/515. Batch 200–209 had 1 base signal and it was decisively BLOCKED. Batch 210–219 had no base signals. Batch 220–229 had 2 base signals: 1 decisively BLOCKED and 1 undecidable because historical BTC 3h return and orderbook ratio were unavailable.

At offset 230 the next expensive signal is `ICXUSDT`, timestamp `1788112800`. Its persistent full-universe breadth build has reached 100/515 and is progressing without timeout. Strategy checkpoint remains 230 until breadth finalization. Aggregate R2 Traffic remains pending.

## 2026-09-06 — ICX first breadth finalized; second ICX timestamp started

At strategy offset 230, `ICXUSDT` signal timestamp `1788112800` completed its full 515-symbol breadth construction without timeout. Replay then encountered another `ICXUSDT` base signal at timestamp `1788114000` that also requires full historical breadth. Its persistent breadth checkpoint has reached 75/515.

Strategy offset intentionally remains 230 because the 10-symbol strategy batch is not complete yet. The first ICX breadth cache is finalized and will be reused on replay retries; only the second timestamp is currently pending. No aggregate Traffic conclusion yet.

## 2026-09-06 — LONG_WINPROTECT_R2 historical Traffic replay COMPLETED

The staged causal replay completed all 15 calendar days, 2026-08-21 through 2026-09-04, over the 515-symbol canonical universe. Final report: `combatsolo.info/storage/live_01/runtime/long_winprotect_r2_history_traffic_report.json`.

Final coverage: 2,877 base LONG signals; 492 decidable (17.10%); 2,385 missing. Missingness is dominated by historical orderbook ratio coverage (2,380 signals), with BTC 3h return missing on 23 and breadth missing on 4. Historical orderbook coverage begins too late for most of the requested window, so the whole-period Traffic-vs-all-base number is coverage-limited.

Among the 492 causally decidable signals, unchanged R2 allowed 143 and blocked 349: Traffic 29.07% among decidable signals. Against all 2,877 base signals, allowed Traffic is 4.97%, or 9.53 allowed signals/day averaged over all 15 calendar days. These are Traffic sanity metrics only, not profitability validation.

Daily decidable Traffic after orderbook history becomes usable: Aug30 37.50% (3/8), Aug31 18.75% (15/80), Sep1 50.72% (35/69), Sep2 34.58% (37/107), Sep3 18.64% (22/118), Sep4 28.18% (31/110). The profile therefore appears materially restrictive on the replayable recent segment; profitability/outcome replay is still required before any promotion decision.

Safety remained unchanged throughout: analysis-only, Active/DEMO/LIVE unchanged, no trading-state writes.

## 2026-09-06 — SOLO guarded outcome-replay task scaffold added

Added fixed guarded task `long_winprotect_r2_outcome_replay` to the SOLO LAB workspace-action allowlist and OpenAPI enum. Its fixed SOLO entrypoint is `tredersolo.online/modules/history_fingerprint_lab/lib/LongWinProtectR2OutcomeReplay.php`; it delegates only to the fixed CombatSolo analysis runner `combatsolo.info/engine/strategies/long_winprotect_r2_outcome_replay.php` / `combatsolo_run_long_winprotect_r2_outcome_replay`. No arbitrary command execution is introduced. Until the fixed outcome runner is installed, the task returns `RUNNER_NOT_INSTALLED`. Purpose: causal historical outcome/PnL comparison of ORIGINAL LONG base signals versus unchanged `LONG_WINPROTECT_R2`, without changing Active/DEMO/LIVE state.

The fixed CombatSolo runner is now installed at `combatsolo.info/engine/strategies/long_winprotect_r2_outcome_replay.php` and the guarded task starts successfully. Research contract is explicit: next confirmed 5m open, 5x leverage, -10% ROI loss threshold / +10% ROI win threshold, 24h horizon, conservative LOSS on an ambiguous same-5m-bar dual touch. This is an OHLC first-touch research proxy, not a claim of exact tick-level DEMO PnL reproduction. Replay starts on 2026-08-30 because earlier R2 orderbook coverage is absent. Outcome replay completed for 2026-08-30 through 2026-09-04. Final report: `combatsolo.info/storage/live_01/runtime/long_winprotect_r2_outcome_report.json`. On the explicit OHLC first-touch proxy contract there were 715 base signals, 492 R2-decidable, 491 outcome-resolved, 143 allowed / 348 blocked. ORIGINAL resolved directional sample: 246 WIN / 244 LOSS (+1 neutral), WR 50.20%. R2 allowed directional sample: 74 WIN / 68 LOSS (+1 neutral), WR 52.11%. R2 captured 176/244 losses (72.13%) but also blocked 172/246 wins (69.92% win damage); net saved-loss-minus-foregone-win count is only +4. Traffic among resolved signals was 29.12%. Interpretation: R2 is highly restrictive and improves proxy WR by only ~1.91 percentage points while discarding roughly 70% of both wins and losses, so this replay does not support promotion.

## 2026-09-06 — balanced LONG candidate league R3 started

Added fixed guarded SOLO research task `long_profile_league_r3_builder` with implementation `combatsolo.info/engine/strategies/long_profile_league_r3_builder.php`. It rebuilds the same causal Aug30-Sep4 resolved sample, uses only causally available R2-space features, applies a 70/30 temporal train/holdout split, searches threshold candidates, and ranks them with a balanced objective combining allowed WR, LOSS capture, Traffic, WIN preservation, plus a train/holdout stability penalty. Candidate Traffic is gated at >=25% in both train and holdout. Output is SHADOW-only; no autoactivation or trading-state writes. The guarded R3 league replay completed across 2026-08-30..2026-09-04 with 490 directional resolved rows (343 train / 147 temporal holdout). Report: `combatsolo.info/storage/live_01/runtime/long_profile_league_r3_report.json`. Best balanced single-rule candidate blocks when `research_btc_ret_3h_pct >= 0.48181965268727645`: full sample allow 345/490 (Traffic 70.41%), 180W/165L, WR 52.17%, loss capture 32.38%, win damage 26.83%, net saved-minus-foregone +13; holdout allow 106/147, 61W/45L, WR 57.55%, Traffic 72.11%, loss capture 32.84%, win damage 23.75%, net +3. A more aggressive candidate at BTC 3h >= -0.14852374839537941 reaches full WR 56.25% but Traffic falls to 32.65% and win damage rises to 63.41%, so it is not preferred as the balanced champion. All outputs remain SHADOW-only; no autoactivation/trading-state writes.

## 2026-09-06 — R4 two-condition search

Because R3 remained near coin-flip on the full sample, added and ran guarded `long_profile_league_r4_multirule`. It searches two-condition OR filters across distinct causal R2-space features and requires holdout WR >=55%, holdout allowed sample >=35, Traffic >=25% on train/holdout, and positive holdout saved-loss-minus-foregone-win. Report: `combatsolo.info/storage/live_01/runtime/long_profile_league_r4_multirule_report.json`. Best scored R4 reaches full WR 56.55% but only 34.29% Traffic and destroys 61.38% of winners; holdout WR 61.29% is based on 62 allowed rows and net holdout value is only +1. A more balanced R4 candidate (`breadth <= -1.34619 OR BTC 3h >= 0.37243`) has full WR 53.49%, Traffic 52.65%, holdout WR 57.78% on 90 allowed rows, but still 43.90% win damage. Conclusion: combining the current five R2-space features still does not produce a robust 60%+ full-sample edge at healthy Traffic. Do not promote; next research generation must broaden the causal historical feature space rather than further threshold-tuning these same five fields.

## 2026-09-06 — G4 replayable DATASET market screening started

Extended `DatasetAsOfFeatureReaderV1` with read-only `collectG4Market()` using historical Parser2 market snapshots at or before decision time. Added replayable fields for price/index/mark, 1h/24h returns, 24h range position/width, spread, top-book sizes/imbalance, volume/turnover and deltas, OI/OI value and delta, funding, mark-index basis, tick age and activity-state flags. No current Passport leakage is used. Added and ran guarded `long_profile_league_g4_market`; 489/490 outcome rows had sufficient market snapshots, producing 38 numeric causal features with 342 train / 147 temporal holdout. This first G4 pass is deliberately single-feature screening before multi-condition search. Stronger signals emerged than in R2 space: `g4_turnover24h_delta_pct <= 0.0761164` gives full WR 55.56% at 49.69% Traffic and holdout WR 58.33%; `g4_ret_1h_pct >= -5.54374` gives full WR 54.64% at 59.51% Traffic and holdout WR 60.92% on 87 allowed rows; `g4_bid1_size <= 421` gives full WR 54.42% at 60.12% Traffic and holdout WR 59.55%. None is promotion-grade alone. Next step is multi-condition G4 search across the expanded vector with strict temporal holdout and practical Traffic constraints. Research-only; Active/DEMO/LIVE unchanged.

## 2026-09-06 — G4 multi-condition search completed

Added and ran guarded `long_profile_league_g4_multirule`, searching 2-3 clause OR filters over 38 replayable causal features (90 pre-ranked threshold atoms), with temporal 70/30 split, Traffic >=30%, holdout allowed >=45 and positive holdout net filter value. This is the first historical search in this sequence to produce a materially non-coin-flip region on both train and holdout. Top balanced candidate: block when `g4_funding_rate <= 0.00003592 OR g4_tick_age_sec >= 120 OR g4_prev_price_24h >= 0.38`. Train: 64W/42L, WR 60.38%, Traffic 30.99%, net +34. Holdout: 63W/33L, WR 65.63%, Traffic 65.31%, loss capture 50.75%, win damage 21.25%, net +17. Full 489: 127W/75L, WR 62.87%, Traffic 41.31%, loss capture 69.26%, win damage 48.16%, net +51. Another candidate (`turnover24h_delta <= 0.00682174 OR BTC3h >= 0.48182 OR funding_rate <= 0.00003592`) reaches full WR 64.50% at 34.56% Traffic; train WR 62.14%, holdout WR 68.18%, but win damage remains high at 55.51%. These are promising research candidates, not promotion evidence: sample covers only six replayable days and uses approximate OHLC outcome proxy. Next required step is robustness testing by day/regime, feature-ablation/semantic checks (especially absolute-price thresholds and tick-age), and forward SHADOW before any champion consideration. Active/DEMO/LIVE unchanged.

## 2026-09-06 — G4 robustness / ablation gate

Added and ran guarded `long_profile_league_g4_robustness` over unique G4 multirule candidates. It evaluates day-by-day behavior, leave-one-day-out stability, one-feature ablations, and rejects formulas containing absolute-price thresholds from the pre-SHADOW gate. 36 unique candidates tested; 6 pass the initial robustness gate. The strongest semantically clean candidate is `turnover24h_delta <= 0.00682174 OR funding_rate <= 0.00003592 OR BTC3h >= 0.48182`: full 109W/60L, WR 64.50%, Traffic 34.56%, net +48. Leave-one-day-out WR remains 61.36%-66.03%. However daily behavior exposes a weak Aug31 regime: 6W/7L (46.15%) with only 16.25% Traffic; Sep1 54.55%, Sep2 75.68%, Sep3 68.75%, Sep4 65.0%. Ablation is informative: removing any one of its three clauses drops full WR to 56.39%-57.55%, so the measured edge is interaction-dependent rather than one dominant threshold. A cleaner basis variant (`turnover24h_delta <= 0.00682174 OR BTC3h >= 0.37243 OR mark_index_basis <= -0.29822`) has full WR 60.89%, Traffic 36.61%, no bad evaluated day (<52% with >=10 allows), but Sep4 net is -3 and several days have low Traffic. The earlier top candidate using `prev_price_24h >= 0.38` plus tick-age is rejected from pre-SHADOW qualification because it depends on an absolute-price threshold and shows severe day instability (Sep1 WR 33.33%). Conclusion: there is a promising interaction signal, especially turnover change + BTC phase + funding/basis, but it is regime-sensitive. Next step should build a small SHADOW candidate family from semantically clean survivors rather than one champion, and track regime/day behavior forward. No LIVE promotion.

## 2026-09-06 — G4 forward SHADOW family installed

Materialized three semantically clean robustness survivors into `control/long_shadow_profiles.json` as `LONG_G4_SHADOW_A/B/C`. All are `SHADOW_ONLY`, `operator_selectable=false`, `blocks_real_entry=false`, generation `G4_REPLAYABLE_DATASET`. A = turnover24h delta + funding + BTC3h (historical proxy WR 64.50%, Traffic 34.56%); B = turnover24h delta + BTC3h + mark/index basis (60.89%, 36.61%); C = turnover24h delta + BTC3h + deep-negative funding (60.80%, 35.99%). Extended the LONG DEMO runner only to attach causal DATASET G4 market features and BTC 3h phase before evaluating shadow profiles; added strict missing-feature handling for `G4_REPLAYABLE_DATASET`. This does not block or alter real DEMO entries: profile decisions are stored alongside each owner and later scored from actual closed DEMO PnL by the existing long profile lab stats path. Absolute-price/tick-age candidate was intentionally not installed. Post-change `click_self_test`: PASS, syntax_failures empty, live_submit_enabled=false, long_owner_safe=true, long_open_owners=40. Active SHORT and main LONG behavior unchanged.

## 2026-09-06 — G5 macro-phase screening

While G4-A/B/C accumulate forward SHADOW evidence, added guarded `long_profile_league_g5_macro` and a causal multi-timeframe macro collector. BTC native 5m history supports 15m/1h/3h/6h/24h returns. ETH native Parser2 kline history is not present in the current DATASET storage, so ETH is explicitly unavailable rather than fabricated; the collector falls back to BTC-only completeness. Final G5 sample: 466 resolved rows, 43 features. The added BTC horizons did not beat the current G4 three-condition survivors. Best two-clause candidate overall is bid1_size + symbol 1h return: WR 58.67%, Traffic 48.28%, holdout WR 61.76%. Best notable macro candidate is symbol 1h return + BTC 6h return: full WR 60.0%, Traffic 42.92%, holdout WR 68.66%, but train WR only 55.64% and winner damage 48.05%, so it is not qualified for SHADOW. A BTC1h+breadth candidate reaches 60.40% full WR but only 31.97% Traffic and 61.04% winner damage. Conclusion: multi-timeframe BTC context is informative but does not independently improve robustness enough to expand the current SHADOW family. No new G5 profile installed; G4-A/B/C remain the forward candidates.

## 2026-09-06 — G6 full orderbook/depth screening

Added causal `collectG6Orderbook()` over Parser2 `raw_replayable_v2` depth snapshots: near/wide notionals, ratios/imbalance, 0.1/0.25/0.5/1/2% depth bands, wall distances/notionals, raw depth counts, near-share and wall imbalance. Guarded `long_profile_league_g6_orderbook` completed on 343 resolved rows with complete historical orderbook and 69 total features. Best two-clause candidate is `symbol ret1h >= -5.63918 OR 2% ask depth <= 7378.31`: full 92W/52L, WR 63.89%, Traffic 41.98%, holdout 37W/14L = 72.55%, train WR 59.14%, net +33. This is interesting but not yet superior evidence to G4-A: smaller 343-row orderbook sample, winner damage 47.43%, and holdout net is only +2. Pure orderbook pair (`2% imbalance >= 0.11599 OR 2% ask depth <= 7378.31`) gives only 59.53% full WR despite 65.85% holdout. Orderbook + BTC/turnover combinations mostly remain around 58-60% full WR. Conclusion: depth adds signal, especially low 2% ask-side depth combined with symbol 1h phase, but current two-clause search does not justify adding another SHADOW profile yet. No LIVE/DEMO behavior change. The result is research-only and is not exact tick-level DEMO PnL. Post-install `click_self_test` PASS, syntax_failures empty, live_submit_enabled=false, self_test_side_effects=NONE.

## 2026-09-06 — G6 robustness + G4/depth hybrid

Guarded `long_profile_league_g6_robustness` tested 58 unique G6 candidates with day metrics, leave-one-day-out and ablation; 6 pass the initial gate (full WR>=60, Traffic>=38, net>=20, <=1 bad day, minimum LOSO WR>=57). The headline ret1h + 2% ask-depth candidate passes mathematically (63.89% WR, 41.98% Traffic, min LOSO 59.60%) but has a severe Sep1 day at 35.29% WR and 47.43% winner damage, so it remains research-only. A more even non-orderbook pair bid1_size + range-position has 61.63% WR / 50.15% Traffic with no evaluated bad day, confirming that robustness rather than headline holdout should drive qualification.

Then guarded `long_profile_league_g6_g4_hybrid` explicitly replaced the third G4 regime clause with one depth clause while retaining the clean turnover+BTC backbone. Best hybrid is `turnover24h_delta <= 0.00682174 OR BTC3h >= 0.48182 OR 2% depth imbalance >= 0.07481`: train 61.73% WR at 33.75% Traffic; holdout 67.74% at 60.19% Traffic; full 92W/51L = 64.34% WR at 41.69% Traffic, net +34, and no evaluated day below 52% WR (Aug31 61.54, Sep1 56.25, Sep2 56.25, Sep3 85.71, Sep4 64.29). This is materially cleaner day behavior than the standalone ret1h+ask-depth candidate, but winner damage remains 47.43% and the causal orderbook sample is only 343 rows. It therefore becomes the leading G6 forward-SHADOW candidate, not a LIVE candidate; install/forward tracking requires the LONG runtime to attach G6 orderbook features without affecting real-entry decisions. Existing G4-A/B/C remain unchanged.

## 2026-09-06 — G6-D forward SHADOW installed

Extended the LONG DEMO runner's shadow-only feature payload with causal `collectG6Orderbook()` data. This occurs only before shadow profile evaluation and does not alter the base LONG match or real-entry decision. Added `G6_REPLAYABLE_ORDERBOOK` to strict shadow feature-schema handling and materialized `LONG_G6_SHADOW_D`: block on `turnover24h_delta <= 0.0068217413 OR BTC3h >= 0.48181965 OR 2% depth imbalance >= 0.07481`. Historical proxy reference: 64.34% WR, 41.69% Traffic, 343 resolved rows; forward status is `FORWARD_SHADOW_ROBUSTNESS_SURVIVOR`, `operator_selectable=false`, `blocks_real_entry=false`. G6-D now runs in parallel with G4-A/B/C and its decisions will be scored from subsequent actual closed DEMO owners through the existing profile-lab statistics path. No backfill is treated as forward evidence. Post-install guarded `click_self_test`: PASS, syntax_failures empty, mapping/registry/cross safety true, live_submit_enabled=false, long_owner_safe=true, long_open_owners=40, side effects NONE. Active SHORT and main LONG remain unchanged.

## 2026-09-06 — G7 research target raised to 75% WR

Per research direction, treated ~65% as passed for exploration purposes and launched guarded `long_profile_league_g7_75_target`; this does NOT mean 65% is forward-validated. Search combines G4 market, G5 BTC phase and G6 depth on the 343-row causal orderbook sample, with 70/30 temporal split and a deliberate high-WR objective while retaining minimum Traffic/sample gates. The search found many >=75% historical-proxy formulas, proving that 75% can be reached in-sample/holdout by stronger filtering, but the cost is severe winner destruction and lower Traffic. Top formula: block if `symbol ret1h >= -5.63918 OR ask1_size <=1074 OR 2% ask depth <=7378.31`; train 77.27% WR (34W/10L), holdout 85.71% (18W/3L), full 80.0% (52W/13L), Traffic 18.95%, loss capture 92.26%, winner damage 70.29%, net +32. A somewhat higher-Traffic formula `bid1_size<=3200 OR OI delta>=0.16739 OR turnover delta<=0.0026085` gives train 76.19%, holdout 75.0%, full 75.71%, Traffic 20.41%, winner damage 69.71%, net +29. Critical warning: several top candidates have negative holdout net saved-minus-foregone despite high WR, so 75% WR alone is not qualification. No G7 profile installed to forward SHADOW yet; next step is a stricter 75%-target robustness/Pareto pass prioritizing Traffic, holdout net value and winner preservation before selecting at most a small experimental SHADOW family.

## 2026-09-06 — G7 75%-target Pareto gate

Added and ran guarded `long_profile_league_g7_pareto` over the G7 high-WR candidates, rebuilding the same 343-row causal G4+G5+G6 sample and checking day/leave-one-day-out stability. Qualification required train and holdout WR >=70%, full WR >=72%, Traffic >=18%, non-negative holdout net saved-minus-foregone, full net >=20, winner damage <=72%, <=1 bad evaluated day, and minimum leave-one-day-out WR >=68%. Result: **0 candidates qualified; Pareto frontier empty**. This is the key correction to the raw 75-80% search: every high-WR formula paid too much somewhere, especially negative holdout net value and/or excessive winner destruction. Therefore 75% has been reached as a historical classification/filtering number, but not as a robust practical profile under the current causal feature set/sample. No G7 SHADOW profile installed. Research target remains 75%, but future searches must improve the frontier rather than weaken these gates just to claim the number.

## 2026-09-06 — G8 regime-conditioned search

Changed search topology instead of tightening global thresholds: guarded `long_profile_league_g8_regime` partitions the same causal G4+G5+G6 sample by BTC 3h/1h sign, symbol 1h sign, turnover direction and OI direction, then searches two-clause filters inside each regime with temporal train/holdout. This exposes materially better local frontiers. Most important semantically usable result is BTC3H_NEG: `breadth24h <= 0.01918 OR 2% depth imbalance >= 0.11504` gives train 70.97%, holdout 83.33%, full 76.36% WR at 39.86% Traffic, winner damage 45.45%, net +13. A broader BTC3H_NEG variant `breadth <= -0.10282 OR depth imbalance >=0.11504` gives 73.53% full WR at 49.28% Traffic with only 35.06% winner damage and net +16. BTC1H_POS also produces a clean depth/top-book candidate: `0.1% bid depth >=251.43 OR bid1_size<=242`, full 75.0% WR at 30.34% Traffic, winner damage 52.17%, net +29, holdout 80.95% with net +4. Several apparent 75-79% regime candidates rely on absolute symbol price/turnover thresholds and remain semantically suspect; they are not qualification evidence. Key finding: the 75% frontier becomes much healthier when profiles are conditional on market regime rather than global. G8 robustness/semantic filtering then confirmed the two BTC3H-negative breadth+normalized-depth candidates. The wider candidate remains 73.53% full WR at 49.28% Traffic, holdout 74.19%, with 0 bad evaluated days; leave-one-day-out WR stays 72.73–75.00%. The tighter candidate remains 76.36% full WR at 39.86% Traffic, holdout 83.33%, also 0 bad evaluated days; leave-one-day-out WR stays 72.73–78.72%. Ablation materially weakens both, confirming the breadth/depth interaction rather than one accidental clause. The BTC1H-positive 75% candidate fails the robustness/semantic gate: only 3 evaluated days, one 40% day, and both raw depth/top-book thresholds are scale-sensitive across symbols.

Installed only the two portable BTC3H-negative survivors as `LONG_G8_BTC3H_NEG_WIDE` and `LONG_G8_BTC3H_NEG_75`, both `SHADOW_ONLY`, non-selectable and non-blocking. Added explicit `regime_all` support to the LONG shadow evaluator: a regime profile is scored only while its causal regime conditions hold; outside the regime it records `OUT_OF_REGIME` and is excluded from forward profile statistics rather than being diluted as ALLOW/BLOCK. Feature schema `G8_REGIME_ORDERBOOK` is strict for all regime/rule features. No LIVE/DEMO entry behavior changed.

## 2026-09-06 — G9 FAT-ROI profile research

Added guarded analysis-only `long_profile_fat_roi_g9` to test the user's hypothesis that roughly <=20 high-opportunity alt signals/day can be isolated. Unlike prior WIN/LOSS builders, G9 labels each causal LONG signal by 24h maximum favorable excursion (MFE) from next confirmed 5m open at the existing 5x reference leverage. It screens targets +20%, +30%, and +50% MFE ROI and reports train/holdout precision, Traffic, signals/day, average MFE and adverse excursion. Important: MFE is opportunity, not realized PnL; exit logic is deliberately not credited.

On 187 causally feature-complete rows across 6 days, +20% MFE occurs in 59.36% of the base sample. The search can concentrate these opportunities while staying inside the user's expected traffic range. A portable-ish candidate using `BTC3h <= -0.01719 OR turnover24h_delta >= 0.32996` (block semantics in the research evaluator, so allowed rows are the complement) yields 75.32% +20%-MFE precision, 41.18% Traffic, about 12.83 signals/day, with holdout precision 82.35%. A higher-traffic orderbook/volume candidate yields 73.27% precision at 54.01% Traffic, about 16.83 signals/day. Several top formulas use absolute volume/OI scale and are research-only due cross-symbol portability risk.

For +30% MFE, the best screened family is materially weaker but still enriched: about 65.38% precision at 41.71% Traffic (~13/day) versus a 42.25% base rate. For +50% MFE, the best result is ~50.63% precision at 42.25% Traffic (~13.17/day) versus a 26.20% base rate. Thus the FAT hypothesis is supported as an enrichment problem: large excursions are not rare in this signal universe, and ~10-17 candidate signals/day is realistic in this six-day proxy. However adverse excursion is also large, so no FAT SHADOW profile is installed yet. Next step must model path quality/time-to-MFE/MAE-before-MFE and practical exit capture rather than selecting on MFE alone.

## 2026-09-06 — G10 FAT PnL ranking with cheap -3% ROI stop

Added guarded analysis-only `long_profile_fat_roi_g10_stop3`. It re-ranks G9 FAT candidates by path-based proxy PnL using LONG entry at next confirmed 5m open, 5x reference leverage, hard stop -3% ROI (= -0.6% underlying price), and 24h close for survivors. A stop touch is charged at -3% ROI; fees/slippage are not yet modeled.

Result on 187 rows / 6 days: the -3% stop is extremely tight and triggers on 175/187 (93.58%) of the unfiltered base, but still improves base aggregate 24h-close ROI from -64.95 to -22.73. More importantly, several FAT filters become positive under the cheap-stop asymmetry. Best ranked candidate (`BTC3h <= -0.01719 OR symbol ret24h <= -5.6699` as block rules; allowed set is their complement) produces 78 allowed signals (~13/day), 72 stops (92.31%), aggregate +131.30 ROI points, avg +1.683% ROI/signal; train +0.846% avg and holdout +4.111% avg. Another candidate (`volume24h <= 24.084m OR ret24h <= 1.20195`, scale-sensitive) gives 90 signals (~15/day), aggregate +168.73 ROI points, avg +1.875%/signal, train +1.633%, holdout +2.470%.

Interpretation: selecting by PnL with a very cheap stop is materially more promising than selecting FAT profiles by MFE precision alone. However stop rate >90% means the economics depend on a small number of large survivors and are highly sensitive to fees, slippage, intrabar execution, and exit choice. No SHADOW/LIVE behavior changed. Next research should sweep stop levels around -2/-3/-4/-5 ROI and model practical profit exits/trailing plus fees before forward installation.

## 2026-09-06 — G11 FAT entry-timing diagnosis

Added guarded analysis-only `long_profile_fat_roi_g11_entry` to test whether G10 identifies the right FAT symbols but enters too early for a -3% ROI stop. On the selected FAT subset (97 signals), immediate next-5m entry stopped 91/97 (93.81%). Crucially, among those stopped trades, 59 (64.84%) later reached +20% MFE ROI, 51 (56.04%) later reached +30%, and 39 (42.86%) later reached +50%. This strongly supports an entry-timing problem rather than merely bad symbol selection.

Oracle pullback-low diagnostics show the upper bound from better price location: using the lowest price known retrospectively within 15/30/60 minutes reduces stop rate to 85.57/84.54/78.35% and raises proxy average ROI to +11.85/+12.10/+16.89 per signal. These oracle variants are explicitly non-tradable and exist only to quantify entry-location headroom.

A simple causal-ish diagnostic, first green 5m after a lower low, is weaker than the oracle but materially better than immediate entry: 15m variant covers 79 signals, stop rate 89.87%, aggregate +409.73 ROI points and avg +5.19%/signal versus immediate +91.31 and +0.94%/signal on the broader selected set. 30m/60m variants average +3.88/+3.67% respectively. This is not yet a production entry rule, but confirms that delayed/retest entry deserves a dedicated builder using causal orderbook/geometry confirmation. No SHADOW/LIVE behavior changed.

## 2026-09-15 — LONG DEMO AUTO selector stability repair

Diagnosed a live control-plane loop in `DatasetChampionControl::leader()`: when the current LONG profile had negative/reduced recent evidence, an internal `freshScore` override selected a non-rank leader; on the next tick that replacement could be considered healthy and the selector returned to DATASET rank #1. In AGGRESSIVE DEMO AUTO this caused consecutive profile rewrites such as `LONG_HSCAN_24H_20260909_2000_3B1D37 -> LONG_HSCAN_6H_20260909_1900_FDC29A -> LONG_HSCAN_24H_20260909_2000_3B1D37`. This contradicted the documented AUTO contract and did not occur as a stable mirror of SHORT control.

Replaced the falling/freshScore branch with deterministic eligible-profile ordering: DATASET rank ascending, ranking score descending, profile ID as final stable tie-break. Existing operator anti-stuck profile blocking remains intact. PHP 8.4 syntax check passed. Natural post-deploy ticks converged LONG DEMO to `LONG_HSCAN_6H_20260909_1900_FDC29A`; the following tick reported `current_profile == target_profile`, `reason=SAME_PROFILE`, `profile_switch_required=false`, and `settings_sync_required=false`. LONG runner remained `COMPLETE`, DEMO submit enabled, two managed Buy positions, no management errors. SHORT code/risk and LIVE state were not mutated. Changed file: `combatsolo.info/engine/strategies/dataset_champion_control.php`; automatic mutation backup: `20260915_133302Z_b4bf2323fa56`.


## 2026-09-23 — DEMO/LIVE execution architecture pinned for Jeffrey qualification

Added `docs/DEMO_LIVE_EXECUTION_ANCHOR.md` and made it mandatory in `docs/NEW_CHAT_BOOTSTRAP.md`. Canonical execution invariant is now explicit: DEMO LONG/SHORT is the primary trading/decision contour, writes authoritative `DemoSignalJournal` OPEN/CLOSE events, and LIVE LONG/SHORT is the execution mirror/follower of those DEMO events. DEMO does not mirror LIVE.

Jeffrey engine `live_51d8719f3b4dd6849d031842` is currently in DEMO-first qualification after client 2.0.56. Only LONG is under active execution test; LIVE key is intentionally absent/not used and SHORT is not yet part of the current Jeffrey test. Therefore LIVE OFF/NO KEY/absence of LIVE positions is expected and must not be treated as a mirror failure.

Required qualification order before LIVE enablement is fixed as: repeated DEMO LONG full cycles; DEMO SHORT full cycles; concurrent LONG+SHORT; repeated safe TRADE CONFIG reload `STOP NEW -> WAIT NATURAL FLAT -> APPLY -> REQUALIFY -> RESUME`; AUTO/profile switching for both directions; only then connect LIVE LONG/LIVE SHORT keys and verify DEMO journal OPEN/CLOSE -> matching LIVE execution. Existing positions must continue old-config management to natural FLAT during maintenance; config reload must not force-close positions. `combatsolo.info` remains the read-only reference engine unless the owner explicitly authorizes mutation.

## 2026-09-25 — TRADE CONFIG drain and operator manual-close qualification on Jeffrey

Jeffrey engine `live_51d8719f3b4dd6849d031842` completed a focused qualification pass of the safe TRADE CONFIG transition. Both DEMO directions correctly disabled NEW entries while preserving old-config management for already open positions. SHORT reached DEMO FLAT and advanced into REQUALIFYING. LONG remained in DRAINING while old-config DEMO ownership positions were still active; transition reconciliation was confirmed to run every engine tick and to gate on active ownership states rather than UI rows alone.

Diagnostic bridge repairs were delivered as targeted Jeffrey-only client updates 2.0.72 and 2.0.73: health now reports `instance_id`, DATASET receiver preserves it, and `ClientReleaseUpdater` now initializes `instanceId` so safe-state read requests can return `trade_config_transition.json` and `runtime/last_tick.json`. This confirmed the transition was not stuck: remaining LONG positions were real while SHORT had already reached FLAT/requalification.

Operator manual DEMO close was then qualified for prolonged drain. The first UI path exposed an independent AJAX/live-refresh transport weakness: the per-position close button depended on `/combatsolo/strategy-live`, so a refresh/fetch failure also prevented manual close from reaching the trading control. Targeted 2.0.74/2.0.75 moved the operator close command onto the normal authenticated strategy-page POST path and wired it to the existing `StrategyManualPositionControl`. A valid operator close now returns `CLOSE_SUBMITTED`; final ownership/result/UI completion occurs on later tick/reconcile. A short UI lag is therefore expected and may temporarily show an old row even after open-count/KPI state has already decreased. Do not resubmit solely because of that brief stale row.

Operational rule is now pinned: natural FLAT remains the default for TRADE CONFIG maintenance, but the operator may use the existing per-position DEMO `Закрыть` action when DRAINING becomes operationally excessive. This is not `RESET DEMO`, does not imply resetting all statistics/positions, and must use the normal reduce-only close path. After the last blocking ownership reaches terminal/FLAT state, the transition continues APPLY -> REQUALIFY -> RESUME.

LIVE remains a conditional execution mirror rather than a one-for-one inventory copy of DEMO. DEMO OPEN signals may be skipped in LIVE for key/mode, margin, max-position, gate, ban or other execution reasons. DEMO CLOSE must affect LIVE only when the corresponding DEMO trade actually has an active LIVE mirror/ownership; no blind symbol-level LIVE close is permitted.

Current source-of-truth hierarchy was also corrected: active Jeffrey code/runtime is canonical for current Combat behavior. `combatsolo.info` is obsolete/stale and may be consulted only as historical reference.

## 2026-09-25 — System-wide MAINTENANCE control design pinned

Added canonical design anchor `docs/MAINTENANCE_CONTROL_ANCHOR.md` for a dedicated system-wide maintenance contour, separate from TRADE CONFIG reload and RESET DEMO.

Pinned semantics: `MAINTENANCE STOP` blocks all new DEMO entries and switches LIVE LONG/SHORT OFF for future entries, but already-open DEMO and LIVE positions continue normal ownership/exchange management until terminal/FLAT. `LIVE OFF` during maintenance therefore means "no new LIVE opens", not "stop managing existing LIVE exposure".

After true system FLAT, maintenance archives historical evidence and cleans the active DEMO epoch: current DEMO statistics, intended-to-reset runtime counters, stale scan/reject/order-calc diagnostics and other transient telemetry. A new `telemetry_epoch` (or equivalent boundary) is required so collectors cannot repopulate old diagnostic failures as current health merely by scanning historical logs.

After archive/clean the engine remains in READY/stopped state. A single explicit `START DEMO SYSTEM` action may re-enable DEMO LONG and DEMO SHORT for the new epoch. LIVE must remain OFF after maintenance and may only be enabled again by an explicit user action in LIVE control. No automatic LIVE resume is allowed even if LIVE was ON before maintenance.

Qualification must cover DEMO-only, mixed LONG/SHORT drain, operator manual DEMO close during prolonged drain, LIVE OFF/no-key, LIVE ON with and without open mirrored positions, non-one-to-one DEMO/LIVE inventories, telemetry epoch reset, and the invariant that START DEMO never enables LIVE.

## 2026-09-25 — LIVE REAL + LIVE SHADOW architecture pinned before migration

Added canonical design anchor `docs/LIVE_EXECUTION_SHADOW_ANCHOR.md`.

Historical read-only inspection of obsolete `combatsolo.info` confirmed that the old architecture had two distinct LIVE-side mechanisms: a real Bybit `live_executor_runner.php` and a separate `live_chain_simulator.php`/`live_sim` contour. The historical simulator could validate real key/auth/balance state but did not send network orders; when balance was insufficient it recorded the expected block and continued the LIVE chain in simulation.

Jeffrey target architecture is now pinned as explicit `LIVE REAL` plus separate `LIVE SHADOW/LIVE_SIM`. Both must link back to DEMO through stable mirror/trade ownership ids, but shadow state must never be stored or interpreted as real exchange ownership. DEMO CLOSE must act only on matching REAL/SHADOW ownership, never blindly by symbol.

A possible later `LIVE_PROBE_SHADOW` mode is documented separately: real key/exchange request validation may be combined with independent shadow lifecycle continuation, but only as an explicit probe mode with strict safety handling for unexpected real order acceptance. Normal LIVE safety gates must not be silently weakened merely to provoke an exchange reject.

Full system MAINTENANCE implementation is intentionally deferred until LIVE REAL + LIVE SHADOW ownership/FLAT semantics are verified sufficiently. Maintenance must eventually account for DEMO LONG/SHORT, LIVE REAL LONG/SHORT, LIVE SHADOW LONG/SHORT and pending order/close/reconcile state; DEMO-only FLAT is not a sufficient system-flat proof once LIVE exists.

The LIVE anchor is migration-critical and must move with Jeffrey/DATASET to the new server. Historical `combatsolo.info` runtime remains reference-only and must not be promoted into active Jeffrey state.

## 2026-09-25 — Jeffrey version boundary pinned for migration

The current Jeffrey `2.0.x` line is now frozen conceptually as the stable DEMO/migration baseline. Before the server move, only critical bugfixes should be added to this line; no broad new architecture should be introduced.

The clean engine on the new host will start as `Jeffrey 2.1.0`. Initial 2.1.0 boot must use clean runtime/telemetry state, no migrated API keys/secrets, and LIVE OFF/NO KEY. API keys will be entered again manually after migration to validate the new host's key-storage and validation path.

LIVE REAL + LIVE SHADOW development/qualification continues on the new host after the clean 2.1.0 baseline is verified. Full system MAINTENANCE implementation remains downstream of verified LIVE REAL + LIVE SHADOW ownership/FLAT semantics.

## 2026-09-25 — Side-local LIVE coin profitability filter pinned for 2.1.0

Added canonical target design `docs/LIVE_COIN_FILTER_ANCHOR.md`.

The coin profitability filter is explicitly side-local: LONG and SHORT eligibility for the same symbol are independent. DEMO remains the evidence sensor. A symbol-side that repeatedly loses or produces little/near-zero economic value in DEMO may be withheld from REAL LIVE for that side while DEMO continues trading it and collecting evidence. The opposite side remains independent.

This is intentionally different from a hard manual/safety/technical ban. Profitability filtering normally blocks only new REAL LIVE exposure; it should not stop DEMO observation, otherwise the system loses the evidence needed for recovery/unban. Exact thresholds, minimum samples, windows and hysteresis are intentionally deferred to 2.1.0 qualification rather than guessed now.

Historical project evidence from 2026-09-07 already supports side-local ban isolation: SHORT blocked-map state must not contaminate LONG and vice versa. The 2.1.0 target extends that isolation into a dedicated LIVE capital-protection admission layer.

The target UI must stop presenting this function as one ambiguous `Общий ban монет`. It must expose LONG/SHORT LIVE filter state, reason/evidence and distinguish profitability blocks from manual/safety bans. A blocked DEMO->LIVE mirror must remain linked as a skipped ownership intent so later DEMO CLOSE becomes `NO_LIVE_MIRROR`/terminal no-op rather than a blind symbol close.

## 2026-09-25 — Jeffrey 2.1.0 UI reference and component separation pinned

Added `docs/UI_INFORMATION_ARCHITECTURE_ANCHOR.md`. The current `/combatsolo/strategies/` page is the canonical visual/information-architecture reference for the new host: clean left navigation, compact system summary, paired LONG/SHORT cards, clear DEMO/LIVE separation and minimal duplication.

The current strategy detail page has accumulated multiple independent systems in one surface: Profile Generator, equity/statistics, positions, manual close, closed trades, Symbol Ban, SHADOW/profile controls, LIVE links/state, package information, DEMO/LIVE key/status and events/history, with duplicate hidden layout blocks also present in the rendered HTML. Jeffrey 2.1.0 should simplify strategy detail and promote independent system components to first-class navigation.

Target navigation promotes at least Symbol Ban/Coin Filter, Profile Generator and LIVE Control as separate components. Strategy pages retain only strategy-local state/actions and deep-link to those components with side/strategy preselected. Do not broadly redesign stable 2.0.x before migration.

Implementation priority was then explicitly pinned: after the clean Jeffrey 2.1.0 baseline is running, LIVE REAL + LIVE SHADOW come first. Broad UI/component moves are downstream work and must not delay LIVE qualification. The documented `/combatsolo/strategies/` design remains the target, but moving Symbol Ban/Profile Generator/LIVE Control into final menu locations happens only after LIVE REAL/SHADOW ownership, OPEN/CLOSE matching and LIVE OFF residual-position management are operationally verified.

A generated UI mockup titled `Jeffrey 2.1.0 · Разнос UI-компонентов (план)` was accepted as a probable candidate / NOT FINAL visual reference. It depicts the likely split into: Strategies home, simplified strategy detail, separate Symbol Ban/Coin Filter, separate Profile Generator, dedicated LIVE Control, and simplified Events/Logs. The mockup is structural rather than pixel-final; exact cards, metrics and labels remain open until after LIVE qualification. The visual candidate must not change the pinned priority: LIVE first, UI/component moves later.

## 2026-09-25 — Full Jeffrey backup audited and clean migration seed built

A full ISPmanager backup of the current Jeffrey host was inspected. The active outer tree is confirmed as release `2.0.76`; the active `engine_tick.php` matches the targeted 2.0.76 SHA. The backup also contains a stale nested `combatsolo/combatsolo` copy, release rollback trees, runtime/telemetry/history, old installer/hotfix artifacts and secret-bearing state.

The backup snapshot was captured during TRADE CONFIG `DRAINING` with LONG `demo_open_remaining=3` and SHORT `demo_open_remaining=1`; therefore snapshot positions/ownership and pending transition state are evidence only and must not be activated on the new host.

A sanitized clean seed was built from the active outer tree. It preserves current code, LONG/SHORT packages, trade settings, Symbol Ban evidence and required non-secret control metadata while forcing DEMO/LIVE OFF, clearing active runtime and excluding Bybit keys, old key bindings, DATASET auth token, cron/admin/session secrets, release backups and stale copies. The instance storage path was made portable and PHP/JSON validation passed cleanly.

A separate no-secrets legacy evidence layer preserves trade/runtime/profile/ban/history material. Detailed findings are recorded in `docs/JEFFREY_BACKUP_AUDIT_2026-09-25.md`. The cleaned code remains a 2.0.76 source baseline for the future 2.1.0 release; same-host DATASET filesystem shortcuts and broader code/UI cleanup remain post-migration work, with LIVE REAL + LIVE SHADOW still the first feature priority.
