fix: isolate overlapping Human Gate loads
This commit is contained in:
parent
9d84d5c5c4
commit
ab329aa126
|
|
@ -10,6 +10,7 @@ function createHumanGates(options = {}) {
|
|||
let reviewSnapshot = [];
|
||||
let reviewIndex = -1;
|
||||
let loadedAccountKey = '';
|
||||
let loadEpoch = 0;
|
||||
const decisionKeys = new Map();
|
||||
let decisionFlight = null;
|
||||
|
||||
|
|
@ -80,6 +81,7 @@ function createHumanGates(options = {}) {
|
|||
}
|
||||
|
||||
async function load() {
|
||||
const epoch = ++loadEpoch;
|
||||
const accountKey = String(getAccountKey() || '').trim().toLowerCase();
|
||||
if (accountKey !== loadedAccountKey) {
|
||||
loadedAccountKey = accountKey;
|
||||
|
|
@ -92,12 +94,14 @@ function createHumanGates(options = {}) {
|
|||
if (cached) { queue = cached; render(); }
|
||||
try {
|
||||
const live = validSnapshot(await fetchJson('api/v1/human-gates'));
|
||||
if (epoch !== loadEpoch || accountKey !== loadedAccountKey) return queue;
|
||||
if (!live) throw new Error('Human Gates response is invalid.');
|
||||
queue = { pending_count: live.pending_count, items: live.items.slice() };
|
||||
save(queue);
|
||||
render();
|
||||
return queue;
|
||||
} catch (error) {
|
||||
if (epoch !== loadEpoch || accountKey !== loadedAccountKey) return queue;
|
||||
if (!cached) throw error;
|
||||
setText(nodes.status, 'Offline cached gate list · reconnect before deciding.');
|
||||
return queue;
|
||||
|
|
|
|||
|
|
@ -94,6 +94,18 @@ const gates=createHumanGates({storage,getLogin:()=> 'timmy',getAccountKey:()=>ac
|
|||
assert "secret" not in output["html"]
|
||||
|
||||
|
||||
def test_stale_account_load_cannot_overwrite_new_account_queue_or_cache():
|
||||
output = run_node(r"""
|
||||
const values=new Map(); const storage={getItem:k=>values.get(k)||null,setItem:(k,v)=>values.set(k,v)};
|
||||
let account='1:timmy', resolveA, resolveB;
|
||||
const responseA=new Promise(resolve=>resolveA=resolve), responseB=new Promise(resolve=>resolveB=resolve);
|
||||
const gates=createHumanGates({storage,getLogin:()=> 'timmy',getAccountKey:()=>account,isOnline:()=>true,nodes:{count:{},list:{innerHTML:''},status:{},panel:{}},location:{hash:''},fetchJson:()=>account.startsWith('1:')?responseA:responseB});
|
||||
(async()=>{const loadA=gates.load();account='2:timmy';const loadB=gates.load();resolveB({pending_count:1,items:[{id:'b',title:'B gate',candidate_hash:'bhash'}]});await loadB;resolveA({pending_count:1,items:[{id:'a-secret',title:'A secret',candidate_hash:'asecret'}]});await loadA;process.stdout.write(JSON.stringify({snapshot:gates.snapshot(),cached:JSON.parse(values.get('stackchain.human-gates.v1:2:timmy'))}));})();
|
||||
""")
|
||||
assert [item["id"] for item in output["snapshot"]["items"]] == ["b"]
|
||||
assert [item["id"] for item in output["cached"]["items"]] == ["b"]
|
||||
|
||||
|
||||
def test_decision_retry_reuses_the_same_idempotency_key():
|
||||
output = run_node(r"""
|
||||
let attempts=0; const keys=[];
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user