mirror of
https://github.com/theodore-song/polymarket-analyst.git
synced 2026-08-24 13:08:10 +00:00
Keep autonomous cycles alive under state growth
This commit is contained in:
@@ -40,7 +40,7 @@ jobs:
|
|||||||
RUNTIME_STATE_PATH: runtime/state.json
|
RUNTIME_STATE_PATH: runtime/state.json
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Wait for the next five-minute slot
|
- name: Wait for the next five-minute slot
|
||||||
if: success()
|
if: always()
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
now=$(date +%s)
|
now=$(date +%s)
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
echo "Next autonomous slot starts in ${delay}s"
|
echo "Next autonomous slot starts in ${delay}s"
|
||||||
sleep "$delay"
|
sleep "$delay"
|
||||||
- name: Dispatch the next autonomous cycle
|
- name: Dispatch the next autonomous cycle
|
||||||
if: success()
|
if: always()
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -16,10 +16,12 @@ const MAX_STATE_BYTES = 900_000;
|
|||||||
const TRANSPORT_HISTORY_LIMIT = 24;
|
const TRANSPORT_HISTORY_LIMIT = 24;
|
||||||
const TRANSPORT_SNAPSHOT_LIMIT = 96;
|
const TRANSPORT_SNAPSHOT_LIMIT = 96;
|
||||||
const TRANSPORT_MAKER_OUTCOME_LIMIT = 30;
|
const TRANSPORT_MAKER_OUTCOME_LIMIT = 30;
|
||||||
const TRANSPORT_TARGET_BYTES = 875_000;
|
const TRANSPORT_TARGET_BYTES = 850_000;
|
||||||
const TRANSPORT_HISTORY_FLOOR = 12;
|
const TRANSPORT_HISTORY_FLOOR = 12;
|
||||||
const TRANSPORT_MAKER_OUTCOME_FLOOR = 12;
|
const TRANSPORT_MAKER_OUTCOME_FLOOR = 12;
|
||||||
const TRANSPORT_SUGGESTION_FLOOR = 240;
|
const TRANSPORT_SUGGESTION_FLOOR = 240;
|
||||||
|
const TRANSPORT_SIGNAL_OUTCOME_RESERVE = 144;
|
||||||
|
const TRANSPORT_SNAPSHOT_FALLBACKS = Object.freeze([72, 48, 24]);
|
||||||
|
|
||||||
function sampleSnapshots(rows, limit = TRANSPORT_SNAPSHOT_LIMIT) {
|
function sampleSnapshots(rows, limit = TRANSPORT_SNAPSHOT_LIMIT) {
|
||||||
const list = Array.isArray(rows) ? rows : [];
|
const list = Array.isArray(rows) ? rows : [];
|
||||||
@@ -113,24 +115,92 @@ export function compactRuntimeTransportSnapshot(snapshot) {
|
|||||||
portfolio.lastDecision = compactDecision(portfolio.lastDecision);
|
portfolio.lastDecision = compactDecision(portfolio.lastDecision);
|
||||||
}
|
}
|
||||||
suggestions.suggestions = (suggestions.suggestions || []).slice(0, 300).map(compactPublicSuggestion);
|
suggestions.suggestions = (suggestions.suggestions || []).slice(0, 300).map(compactPublicSuggestion);
|
||||||
|
const writeItems = () => {
|
||||||
out.items[AGENTS_KEY] = JSON.stringify(state);
|
out.items[AGENTS_KEY] = JSON.stringify(state);
|
||||||
out.items[SUGGESTIONS_KEY] = JSON.stringify(suggestions);
|
out.items[SUGGESTIONS_KEY] = JSON.stringify(suggestions);
|
||||||
if (Buffer.byteLength(JSON.stringify(out)) > TRANSPORT_TARGET_BYTES) {
|
return Buffer.byteLength(JSON.stringify(out));
|
||||||
|
};
|
||||||
|
let bytes = writeItems();
|
||||||
|
if (bytes > TRANSPORT_TARGET_BYTES) {
|
||||||
for (const portfolio of Object.values(state.agents || {})) {
|
for (const portfolio of Object.values(state.agents || {})) {
|
||||||
portfolio.history = (portfolio.history || []).slice(-TRANSPORT_HISTORY_FLOOR);
|
portfolio.history = (portfolio.history || []).slice(-TRANSPORT_HISTORY_FLOOR);
|
||||||
portfolio.maker_outcomes = (portfolio.maker_outcomes || []).slice(-TRANSPORT_MAKER_OUTCOME_FLOOR);
|
portfolio.maker_outcomes = (portfolio.maker_outcomes || []).slice(-TRANSPORT_MAKER_OUTCOME_FLOOR);
|
||||||
}
|
}
|
||||||
suggestions.suggestions = suggestions.suggestions.slice(0, TRANSPORT_SUGGESTION_FLOOR);
|
suggestions.suggestions = suggestions.suggestions.slice(0, TRANSPORT_SUGGESTION_FLOOR);
|
||||||
out.items[AGENTS_KEY] = JSON.stringify(state);
|
bytes = writeItems();
|
||||||
out.items[SUGGESTIONS_KEY] = JSON.stringify(suggestions);
|
|
||||||
}
|
}
|
||||||
if (out.summary && out.summary.signal_ledger && state.signal_ledger) {
|
const ledger = state.signal_ledger && typeof state.signal_ledger === "object" ? state.signal_ledger : null;
|
||||||
const pending = Array.isArray(state.signal_ledger.pending) ? state.signal_ledger.pending.length : 0;
|
const pending = Array.isArray(ledger?.pending) ? ledger.pending : [];
|
||||||
const outcomes = Array.isArray(state.signal_ledger.outcomes) ? state.signal_ledger.outcomes.length : 0;
|
const outcomes = Array.isArray(ledger?.outcomes) ? ledger.outcomes : [];
|
||||||
out.summary.signal_ledger.pending_retained = pending;
|
if (bytes > TRANSPORT_TARGET_BYTES && ledger) {
|
||||||
out.summary.signal_ledger.pending_total = Math.max(Number(out.summary.signal_ledger.pending_total || 0), pending);
|
ledger.pending = [];
|
||||||
out.summary.signal_ledger.outcomes_retained = outcomes;
|
ledger.outcomes = outcomes.slice(-Math.min(TRANSPORT_SIGNAL_OUTCOME_RESERVE, outcomes.length));
|
||||||
out.summary.signal_ledger.outcomes_total = Math.max(Number(out.summary.signal_ledger.outcomes_total || 0), outcomes);
|
bytes = writeItems();
|
||||||
|
|
||||||
|
for (const limit of TRANSPORT_SNAPSHOT_FALLBACKS) {
|
||||||
|
if (bytes <= TRANSPORT_TARGET_BYTES) break;
|
||||||
|
for (const portfolio of Object.values(state.agents || {})) {
|
||||||
|
portfolio.snapshots = sampleSnapshots(portfolio.snapshots, limit);
|
||||||
|
}
|
||||||
|
bytes = writeItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes > TRANSPORT_TARGET_BYTES && ledger.outcomes.length) {
|
||||||
|
let low = 0;
|
||||||
|
let high = ledger.outcomes.length;
|
||||||
|
while (low < high) {
|
||||||
|
const mid = Math.ceil((low + high) / 2);
|
||||||
|
ledger.outcomes = outcomes.slice(-mid);
|
||||||
|
if (writeItems() <= TRANSPORT_TARGET_BYTES) low = mid;
|
||||||
|
else high = mid - 1;
|
||||||
|
}
|
||||||
|
ledger.outcomes = low ? outcomes.slice(-low) : [];
|
||||||
|
bytes = writeItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
let pendingLow = 0;
|
||||||
|
let pendingHigh = pending.length;
|
||||||
|
while (pendingLow < pendingHigh) {
|
||||||
|
const mid = Math.ceil((pendingLow + pendingHigh) / 2);
|
||||||
|
ledger.pending = pending.slice(0, mid);
|
||||||
|
if (writeItems() <= TRANSPORT_TARGET_BYTES) pendingLow = mid;
|
||||||
|
else pendingHigh = mid - 1;
|
||||||
|
}
|
||||||
|
ledger.pending = pending.slice(0, pendingLow);
|
||||||
|
bytes = writeItems();
|
||||||
|
|
||||||
|
if (pendingLow === pending.length && ledger.outcomes.length < outcomes.length) {
|
||||||
|
let outcomeLow = ledger.outcomes.length;
|
||||||
|
let outcomeHigh = outcomes.length;
|
||||||
|
while (outcomeLow < outcomeHigh) {
|
||||||
|
const mid = Math.ceil((outcomeLow + outcomeHigh) / 2);
|
||||||
|
ledger.outcomes = outcomes.slice(-mid);
|
||||||
|
if (writeItems() <= TRANSPORT_TARGET_BYTES) outcomeLow = mid;
|
||||||
|
else outcomeHigh = mid - 1;
|
||||||
|
}
|
||||||
|
ledger.outcomes = outcomeLow ? outcomes.slice(-outcomeLow) : [];
|
||||||
|
writeItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const syncLedgerSummary = () => {
|
||||||
|
if (!out.summary?.signal_ledger || !ledger) return;
|
||||||
|
out.summary.signal_ledger.pending_retained = ledger.pending.length;
|
||||||
|
out.summary.signal_ledger.pending_total = Math.max(Number(out.summary.signal_ledger.pending_total || 0), pending.length);
|
||||||
|
out.summary.signal_ledger.outcomes_retained = ledger.outcomes.length;
|
||||||
|
out.summary.signal_ledger.outcomes_total = Math.max(Number(out.summary.signal_ledger.outcomes_total || 0), outcomes.length);
|
||||||
|
out.summary.signal_ledger.byte_budget = TRANSPORT_TARGET_BYTES;
|
||||||
|
};
|
||||||
|
syncLedgerSummary();
|
||||||
|
bytes = writeItems();
|
||||||
|
while (bytes > TRANSPORT_TARGET_BYTES && ledger?.pending.length) {
|
||||||
|
ledger.pending.pop();
|
||||||
|
syncLedgerSummary();
|
||||||
|
bytes = writeItems();
|
||||||
|
}
|
||||||
|
while (bytes > TRANSPORT_TARGET_BYTES && ledger?.outcomes.length) {
|
||||||
|
ledger.outcomes.shift();
|
||||||
|
syncLedgerSummary();
|
||||||
|
bytes = writeItems();
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ assert.match(api, /searchParams\.set\("runtime", `\$\{Date\.now\(\)\}/);
|
|||||||
assert.match(workflow, /cron: "2,7,12,17,22,27,32,37,42,47,52,57 \* \* \* \*"/);
|
assert.match(workflow, /cron: "2,7,12,17,22,27,32,37,42,47,52,57 \* \* \* \*"/);
|
||||||
assert.match(workflow, /contents: write/);
|
assert.match(workflow, /contents: write/);
|
||||||
assert.match(workflow, /EXPECTED_BUILD: "124"/);
|
assert.match(workflow, /EXPECTED_BUILD: "124"/);
|
||||||
|
assert.equal((workflow.match(/if: always\(\)/g) || []).length, 2);
|
||||||
assert.match(routingReleaseWorkflow, /name: Release protected agent routing after Vercel quota reset/);
|
assert.match(routingReleaseWorkflow, /name: Release protected agent routing after Vercel quota reset/);
|
||||||
assert.match(routingReleaseWorkflow, /routesExclusiveNoPairToTailAlpha/);
|
assert.match(routingReleaseWorkflow, /routesExclusiveNoPairToTailAlpha/);
|
||||||
assert.match(routingReleaseWorkflow, /cron: "25 1-23\/2 23 8 \*"/);
|
assert.match(routingReleaseWorkflow, /cron: "25 1-23\/2 23 8 \*"/);
|
||||||
@@ -177,7 +178,7 @@ assert.match(runner, /MAX_STATE_BYTES = 900_000/);
|
|||||||
assert.match(runner, /compactRuntimeTransportSnapshot\(rawSnapshot\)/);
|
assert.match(runner, /compactRuntimeTransportSnapshot\(rawSnapshot\)/);
|
||||||
assert.match(runner, /localStorage\.getItem\(agentKey\)/);
|
assert.match(runner, /localStorage\.getItem\(agentKey\)/);
|
||||||
assert.match(runner, /localStorage\.getItem\(suggestionsKey\)/);
|
assert.match(runner, /localStorage\.getItem\(suggestionsKey\)/);
|
||||||
assert.match(runner, /TRANSPORT_TARGET_BYTES = 875_000/);
|
assert.match(runner, /TRANSPORT_TARGET_BYTES = 850_000/);
|
||||||
assert.match(runner, /TRANSPORT_SUGGESTION_FLOOR = 240/);
|
assert.match(runner, /TRANSPORT_SUGGESTION_FLOOR = 240/);
|
||||||
assert.match(runner, /Production already advanced to Build \$\{status\.build\}; retiring stale Build \$\{expectedBuild\} runner/);
|
assert.match(runner, /Production already advanced to Build \$\{status\.build\}; retiring stale Build \$\{expectedBuild\} runner/);
|
||||||
assert.equal(resolutionAudit.strategy, "resolution-window-no-50-55-forward-shadow-v3");
|
assert.equal(resolutionAudit.strategy, "resolution-window-no-50-55-forward-shadow-v3");
|
||||||
@@ -268,4 +269,39 @@ assert.equal(transportOutput.summary.signal_ledger.pending_retained, 50);
|
|||||||
assert.equal(transportOutput.summary.signal_ledger.outcomes_retained, 144);
|
assert.equal(transportOutput.summary.signal_ledger.outcomes_retained, 144);
|
||||||
assert.ok(Buffer.byteLength(JSON.stringify(transportOutput)) < Buffer.byteLength(JSON.stringify(transportInput)));
|
assert.ok(Buffer.byteLength(JSON.stringify(transportOutput)) < Buffer.byteLength(JSON.stringify(transportInput)));
|
||||||
|
|
||||||
|
const stressState = structuredClone(transportState);
|
||||||
|
stressState.signal_ledger = {
|
||||||
|
pending: Array.from({ length: 600 }, (_, index) => ({
|
||||||
|
key: `stress-pending-${index}`,
|
||||||
|
market_id: `pending-market-${index}`,
|
||||||
|
observed_at: new Date(1_700_000_000_000 + index * 1_000).toISOString(),
|
||||||
|
side: "YES",
|
||||||
|
learning_payload: "p".repeat(1_200),
|
||||||
|
})),
|
||||||
|
outcomes: Array.from({ length: 1_000 }, (_, index) => ({
|
||||||
|
key: `stress-outcome-${index}`,
|
||||||
|
market_id: `outcome-market-${index}`,
|
||||||
|
evaluated_at: new Date(1_700_000_000_000 + index * 1_000).toISOString(),
|
||||||
|
return: index / 10_000,
|
||||||
|
learning_payload: "o".repeat(1_200),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
const stressInput = {
|
||||||
|
...transportInput,
|
||||||
|
summary: { signal_ledger: { pending_retained: 600, pending_total: 600, outcomes_retained: 1_000, outcomes_total: 1_000 } },
|
||||||
|
items: { ...transportInput.items, pma_agents_v2: JSON.stringify(stressState) },
|
||||||
|
};
|
||||||
|
const stressOutput = compactRuntimeTransportSnapshot(stressInput);
|
||||||
|
const stressedState = JSON.parse(stressOutput.items.pma_agents_v2);
|
||||||
|
assert.ok(Buffer.byteLength(JSON.stringify(stressOutput)) <= 850_000);
|
||||||
|
assert.equal(stressedState.agents.value.positions[0].market_id, "position-0");
|
||||||
|
assert.ok(stressedState.signal_ledger.pending.length > 0);
|
||||||
|
assert.ok(stressedState.signal_ledger.outcomes.length > 0);
|
||||||
|
assert.equal(stressedState.signal_ledger.pending[0].key, "stress-pending-0");
|
||||||
|
assert.equal(stressedState.signal_ledger.outcomes.at(-1).key, "stress-outcome-999");
|
||||||
|
assert.equal(stressOutput.summary.signal_ledger.pending_retained, stressedState.signal_ledger.pending.length);
|
||||||
|
assert.equal(stressOutput.summary.signal_ledger.pending_total, 600);
|
||||||
|
assert.equal(stressOutput.summary.signal_ledger.outcomes_retained, stressedState.signal_ledger.outcomes.length);
|
||||||
|
assert.equal(stressOutput.summary.signal_ledger.outcomes_total, 1_000);
|
||||||
|
|
||||||
console.log(`autonomous runtime verified for Build ${build}`);
|
console.log(`autonomous runtime verified for Build ${build}`);
|
||||||
|
|||||||
Reference in New Issue
Block a user