Keep adaptive evidence read-only offline

This commit is contained in:
Theodore Song
2026-08-22 08:14:19 -04:00
parent d9e1e29a8d
commit 0496bb1d9f
3 changed files with 15 additions and 5 deletions
+3
View File
@@ -19,6 +19,9 @@ Vercel Blob. Build 73 also installs an offline app shell and caches timestamped
market snapshots. During an outage, cycles continue locally; cached entries are market snapshots. During an outage, cycles continue locally; cached entries are
allowed for 90 minutes, older snapshots become mark-only, and all cached data allowed for 90 minutes, older snapshots become mark-only, and all cached data
expires after 24 hours. expires after 24 hours.
Offline cycles can apply calibration already earned from live observations, but
the evidence ledger is read-only: cached prices cannot grade pending signals,
expire horizons, or create new observations.
Build 121 targets five-minute slots with a serialized, self-chained GitHub Actions runtime. Build 121 targets five-minute slots with a serialized, self-chained GitHub Actions runtime.
It continues from the previous agent snapshot and runs the next due paper cycle It continues from the previous agent snapshot and runs the next due paper cycle
+10 -5
View File
@@ -2572,7 +2572,8 @@ function pendingSignalMarketIds(ledger,knownIds=new Set(),now=Date.now()){
} }
return ids; return ids;
} }
function updateSignalLedger(st,markets,suggestions){ function updateSignalLedger(st,markets,suggestions,{liveEvidence=true}={}){
if(!liveEvidence)return st.signal_ledger&&typeof st.signal_ledger==="object"?st.signal_ledger:defaultSignalLedger();
const ledger=st.signal_ledger&&typeof st.signal_ledger==="object"?st.signal_ledger:defaultSignalLedger(); const ledger=st.signal_ledger&&typeof st.signal_ledger==="object"?st.signal_ledger:defaultSignalLedger();
ledger.pending=Array.isArray(ledger.pending)?ledger.pending:[];ledger.outcomes=Array.isArray(ledger.outcomes)?ledger.outcomes:[]; ledger.pending=Array.isArray(ledger.pending)?ledger.pending:[];ledger.outcomes=Array.isArray(ledger.outcomes)?ledger.outcomes:[];
const now=Date.now(),marketMap=Object.fromEntries((markets||[]).map(m=>[String(m.id),m])),stillPending=[]; const now=Date.now(),marketMap=Object.fromEntries((markets||[]).map(m=>[String(m.id),m])),stillPending=[];
@@ -4530,10 +4531,8 @@ async function runDailyCycle(){
?(freshById[String(id)]||((analysisIds.has(String(id)))?cachedById[String(id)]:null)) ?(freshById[String(id)]||((analysisIds.has(String(id)))?cachedById[String(id)]:null))
:(cachedById[String(id)]||null); :(cachedById[String(id)]||null);
if(runMode==="live")saveMarketCache(analysisMarkets,sugs,priceMap); if(runMode==="live")saveMarketCache(analysisMarkets,sugs,priceMap);
if(runMode==="live"){ const supplemental=runMode==="live"?dueSignalIds.map(id=>freshById[String(id)]).filter(Boolean):[];
const supplemental=dueSignalIds.map(id=>freshById[String(id)]).filter(Boolean); updateSignalLedger(st,[...analysisMarkets,...supplemental],sugs,{liveEvidence:runMode==="live"});
updateSignalLedger(st,[...analysisMarkets,...supplemental],sugs);
}
const marketLearning=buildSignalCalibration(st.signal_ledger); const marketLearning=buildSignalCalibration(st.signal_ledger);
const marketLearningByAgent=Object.fromEntries(AGENTS.map(agent=>[agent.id,buildSignalCalibration(st.signal_ledger,agent.id)])); const marketLearningByAgent=Object.fromEntries(AGENTS.map(agent=>[agent.id,buildSignalCalibration(st.signal_ledger,agent.id)]));
if(runMode==="live"){ if(runMode==="live"){
@@ -6843,6 +6842,11 @@ function runEngineSelfTest(){
const ledgerState={signal_ledger:{pending:[{key:"ledger-test",market_id:"ledger-test",observed_at:hoursAgo(25),side:"YES",entry_price:0.40, const ledgerState={signal_ledger:{pending:[{key:"ledger-test",market_id:"ledger-test",observed_at:hoursAgo(25),side:"YES",entry_price:0.40,
signal_type:"trend",quality:"confirmed",category:"Politics"}],outcomes:[]}}; signal_type:"trend",quality:"confirmed",category:"Politics"}],outcomes:[]}};
updateSignalLedger(ledgerState,[market({id:"ledger-test",yes_price:0.50,no_price:0.50})],[]); updateSignalLedger(ledgerState,[market({id:"ledger-test",yes_price:0.50,no_price:0.50})],[]);
const offlineEvidenceState={signal_ledger:{pending:[{key:"offline-ledger",market_id:"offline-ledger",observed_at:hoursAgo(25),side:"YES",entry_price:0.40,
signal_type:"trend",quality:"confirmed",category:"Politics"}],outcomes:[],expired_ungraded:2}};
const offlineEvidenceBefore=JSON.stringify(offlineEvidenceState.signal_ledger);
updateSignalLedger(offlineEvidenceState,[market({id:"offline-ledger",yes_price:0.90,no_price:0.10})],[{market_id:"offline-new",side:"YES",entry_price:0.42,
signal_type:"trend",quality:"confirmed",signal_confidence:0.99,jump_risk:false,category:"Politics"}],{liveEvidence:false});
const compactedLedgerState={signal_ledger:compactPublicSignalLedger({pending:[{key:"ledger-compacted",market_id:"ledger-compacted", const compactedLedgerState={signal_ledger:compactPublicSignalLedger({pending:[{key:"ledger-compacted",market_id:"ledger-compacted",
event_key:"compacted-event",observed_at:hoursAgo(6.5),graded_horizons:[],side:"YES",entry_price:0.40,signal_type:"trend", event_key:"compacted-event",observed_at:hoursAgo(6.5),graded_horizons:[],side:"YES",entry_price:0.40,signal_type:"trend",
quality:"confirmed",category:"Politics",days_to_resolution:30,trade_ready_at_observation:true,fees_enabled:false, quality:"confirmed",category:"Politics",days_to_resolution:30,trade_ready_at_observation:true,fees_enabled:false,
@@ -7461,6 +7465,7 @@ function runEngineSelfTest(){
&&compactedScopedState.signal_ledger.outcomes[0].eligible_agent_ids[0]==="momentum" &&compactedScopedState.signal_ledger.outcomes[0].eligible_agent_ids[0]==="momentum"
&&compactedScopedSuggestion.promoted_for_agents[0]==="momentum", &&compactedScopedSuggestion.promoted_for_agents[0]==="momentum",
offlineCacheRebuildsAgentPromotion:offlineScopedMomentum.trade_ready&&!offlineScopedValue.trade_ready, offlineCacheRebuildsAgentPromotion:offlineScopedMomentum.trade_ready&&!offlineScopedValue.trade_ready,
offlineEvidenceCapabilityIsReadOnly:JSON.stringify(offlineEvidenceState.signal_ledger)===offlineEvidenceBefore,
losingCohortDemotesOnlyEligibleAgent:losingValueState.state==="demoted"&&!losingValueState.allowed losingCohortDemotesOnlyEligibleAgent:losingValueState.state==="demoted"&&!losingValueState.allowed
&&unrelatedMomentumProfile.samples===0&&unrelatedMomentumProfile.current_samples===0, &&unrelatedMomentumProfile.samples===0&&unrelatedMomentumProfile.current_samples===0,
legacyUnscopedEvidenceRemainsReadable:buildSignalCalibration(legacyUnscopedLedger,"momentum").samples===1 legacyUnscopedEvidenceRemainsReadable:buildSignalCalibration(legacyUnscopedLedger,"momentum").samples===1
+2
View File
@@ -42,6 +42,8 @@ assert.match(index, /const DIRECTIONAL_PROBATION_MIN_EVENTS = 12;/);
assert.match(index, /sixHourEvidenceStartsBoundedProbation:/); assert.match(index, /sixHourEvidenceStartsBoundedProbation:/);
assert.match(index, /probationMetadataSurvivesOfflineCompaction:/); assert.match(index, /probationMetadataSurvivesOfflineCompaction:/);
assert.match(index, /probationExitsAtExecutableSixHourPrice:/); assert.match(index, /probationExitsAtExecutableSixHourPrice:/);
assert.match(index, /updateSignalLedger\(st,\[\.\.\.analysisMarkets,\.\.\.supplemental\],sugs,\{liveEvidence:runMode==="live"\}\)/);
assert.match(index, /offlineEvidenceCapabilityIsReadOnly:/);
assert.match(index, /claimedProbationEvents/); assert.match(index, /claimedProbationEvents/);
assert.match(api, /adaptive_probation: s\.adaptive_probation, probation_exit_hours: s\.probation_exit_hours/); assert.match(api, /adaptive_probation: s\.adaptive_probation, probation_exit_hours: s\.probation_exit_hours/);
assert.match(index, /function buildRuntimeFallbackMarketCache\(stored,state\)/); assert.match(index, /function buildRuntimeFallbackMarketCache\(stored,state\)/);