cortex-loop-demo / index.html
cortex-demo
Headline: done is a check result, not a claim
48eff88
Raw History Blame Contribute Delete
9.83 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Cortex Loop - one tick, end to end</title>
<meta name="description" content="A minimal walkthrough of one loop tick: tick, propose, check, try-complete, using a synthetic page-edit ledger.">
<style>
:root{--ink:#111318;--muted:#5b6472;--line:#e3e7ee;--bg:#fbfcfe;--ok:#1a7f4b;--warn:#b45309;--bad:#b91c1c;--accent:#3b4cca}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);font:16px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
.wrap{max-width:960px;margin:0 auto;padding:48px 24px 80px}
h1{font-size:34px;line-height:1.2;margin:0 0 12px;letter-spacing:-.02em}
.sub{color:var(--muted);font-size:17px;margin:0 0 28px;max-width:70ch}
.flow{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 32px;padding:0;list-style:none}
.flow li{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;padding:6px 12px;border:1px solid var(--line);border-radius:999px;background:#fff;color:var(--muted)}
.flow li.on{border-color:var(--accent);color:var(--accent);font-weight:600}
.card{background:#fff;border:1px solid var(--line);border-radius:14px;padding:22px;margin:0 0 20px}
.card h2{font-size:15px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin:0 0 14px}
button{font:inherit;font-weight:600;padding:11px 20px;border-radius:10px;border:1px solid var(--accent);background:var(--accent);color:#fff;cursor:pointer}
button:hover{filter:brightness(1.08)}
button[disabled]{opacity:.45;cursor:not-allowed}
button.ghost{background:#fff;color:var(--accent)}
.row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
table{width:100%;border-collapse:collapse;font-size:14px}
th,td{text-align:left;padding:9px 10px;border-bottom:1px solid var(--line);vertical-align:top}
th{font-size:12px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);font-weight:600}
td.mono,th.mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12.5px}
.pill{display:inline-block;padding:2px 9px;border-radius:999px;font-size:12px;font-weight:600}
.p-pending{background:#eef1f6;color:#475569}
.p-check{background:#fff7e6;color:var(--warn)}
.p-done{background:#e8f7ee;color:var(--ok)}
.p-blocked{background:#fdeaea;color:var(--bad)}
.log{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;background:#0f1420;color:#d7e0ee;border-radius:12px;padding:18px;min-height:190px;white-space:pre-wrap;overflow:auto}
.log .k{color:#8fb4ff}
.log .g{color:#66d19e}
.log .y{color:#f0c86b}
.log .r{color:#ff8f8f}
.foot{color:var(--muted);font-size:14px;margin-top:36px;padding-top:22px;border-top:1px solid var(--line)}
a{color:var(--accent)}
code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;background:#eef1f6;padding:1px 6px;border-radius:5px}
</style>
</head>
<body>
<div class="wrap">
<p style="margin:0 0 6px;font-size:13px;letter-spacing:.1em;text-transform:uppercase;color:#5b6472">Cortex Loop</p><h1>Done is a check result, not a claim.</h1>
<p class="sub" style="font-size:18px;color:#111318">A loop should not be able to finish by asserting that it finished. Here is one that cannot.</p>
<p class="sub"><strong>The problem:</strong> an AI agent can always <em>say</em> it finished. Saying it is free. That is why so much "progress" evaporates on inspection.</p><p class="sub"><strong>What this page proves:</strong> a loop that refuses to mark work complete until the evidence matches. Pick a tick, and watch it succeed on three items and <strong>refuse</strong> on two - because the evidence was not there.</p><p class="sub" style="font-size:15px">No model call, no server: the whole state machine runs in your browser over a synthetic ledger.</p>
<ul class="flow" id="flow">
<li data-p="tick">1 tick</li>
<li data-p="propose">2 propose</li>
<li data-p="check">3 check</li>
<li data-p="complete">4 try-complete</li>
</ul>
<div class="card">
<h2>Synthetic ledger</h2><p style="margin:0 0 14px;padding:10px 14px;background:#fff7e6;border-radius:9px;font-size:14px;color:#7a4b00"><strong>Note:</strong> items <code>edit-2044</code> and <code>edit-2045</code> are designed to <strong>fail</strong> their check. The loop will refuse them. That refusal is the feature.</p>
<table>
<thead><tr><th class="mono">item</th><th>intent</th><th class="mono">state</th></tr></thead>
<tbody id="ledger"></tbody>
</table>
</div>
<div class="card">
<h2>Run one tick</h2>
<div class="row">
<button id="run">Run tick</button>
<button id="reset" class="ghost">Reset ledger</button>
<span id="hint" style="color:var(--muted);font-size:14px">Idle. Press Run tick.</span>
</div>
</div>
<div class="card">
<h2>Trace</h2>
<div class="log" id="log">waiting for tick...</div>
</div>
<p class="foot" style="border-top:none;padding-top:0;margin-top:8px"><strong>Why it matters:</strong> once completion requires evidence, "done" stops being a claim and starts being a check result. That is the difference between an agent that reports progress and one that produces it.</p><p class="foot">
The loop engine is open source: <a href="https://github.com/mobfish-ai/cortex-loop" target="_blank" rel="noopener">cortex-loop</a>
and the harness it drives: <a href="https://github.com/mobfish-ai/cortex-harness-open" target="_blank" rel="noopener">cortex-harness-open</a>.
Example targets come from a public storefront, <a href="https://www.themossriver.com/pages/fit-finder?utm_source=huggingface&amp;utm_medium=space&amp;utm_campaign=cortex_loop_demo" target="_blank" rel="noopener">fit finder</a>
and <a href="https://www.themossriver.com/pages/todays-agent-deal?utm_source=huggingface&amp;utm_medium=space&amp;utm_campaign=cortex_loop_demo" target="_blank" rel="noopener">today's deal</a>.
All ledger rows here are synthetic. No customer records, contact details, hostnames, or internal identifiers are used.
</p>
</div>
<script>
const LEDGER_SEED = [
{id:'edit-2041', intent:'Add a size note beside the buy button', state:'pending'},
{id:'edit-2042', intent:'Point the article entry link at the product page', state:'pending'},
{id:'edit-2043', intent:'Rewrite the promo block copy for the current SKU', state:'pending'},
{id:'edit-2044', intent:'Link a low-traffic landing page into the deal page', state:'pending'},
{id:'edit-2045', intent:'Refresh the stock badge on a variant', state:'pending'}
];
const CHECKS = {
'edit-2041': {evidence:'note rendered in page body near the buy button', pass:true},
'edit-2042': {evidence:'entry anchor resolves to /products/... with tracking', pass:true},
'edit-2043': {evidence:'copy names only the linked product', pass:true},
'edit-2044': {evidence:'landing page returns 200 and contains the link', pass:false},
'edit-2045': {evidence:'variant stock count differs from source of truth', pass:false}
};
let ledger = JSON.parse(JSON.stringify(LEDGER_SEED));
const el = id => document.getElementById(id);
const sleep = ms => new Promise(r => setTimeout(r, ms));
function paint(){
el('ledger').innerHTML = ledger.map(r =>
'<tr><td class="mono">'+r.id+'</td><td>'+r.intent+'</td><td class="mono"><span class="pill p-'+stateClass(r.state)+'">'+r.state+'</span></td></tr>'
).join('');
}
function stateClass(s){
if(s==='pending') return 'pending';
if(s==='checking') return 'check';
if(s==='complete') return 'done';
return 'blocked';
}
function mark(id, s){
const r = ledger.find(x => x.id === id);
if(r) r.state = s;
paint();
}
function phase(p){
document.querySelectorAll('#flow li').forEach(li => li.classList.toggle('on', li.dataset.p === p));
}
function log(html){
const box = el('log');
box.innerHTML += '\n' + html;
box.scrollTop = box.scrollHeight;
}
async function tick(){
el('run').disabled = true;
el('log').textContent = '';
phase('tick');
const next = ledger.find(r => r.state === 'pending');
if(!next){
log('<span class="g">tick: ledger drained, nothing runnable</span>');
el('hint').textContent = 'Ledger drained.';
el('run').disabled = false;
return;
}
log('<span class="k">tick</span> picked '+next.id+' from '+ledger.length+' items (rule: first pending)');
el('hint').textContent = 'Picked ' + next.id;
await sleep(700);
phase('propose');
log('<span class="k">propose</span> '+next.intent+' -> one concrete change, no side effects yet');
await sleep(700);
phase('check');
mark(next.id, 'checking');
log('<span class="k">check</span> evidence: '+CHECKS[next.id].evidence);
await sleep(900);
phase('complete');
const ok = CHECKS[next.id].pass;
if(ok){
mark(next.id, 'complete');
log('<span class="g">try-complete accepted</span> evidence matched -> state=complete');
} else {
mark(next.id, 'blocked');
log('<span class="r">try-complete refused</span> evidence did not match -> state=blocked, tick ends here');
}
await sleep(500);
const left = ledger.filter(r => r.state === 'pending').length;
log('<span class="y">summary</span> completed='+ledger.filter(r=>r.state==='complete').length+
' blocked='+ledger.filter(r=>r.state==='blocked').length+
' pending='+left);
el('hint').textContent = ok ? 'Accepted. Press Run tick again.' : 'Refused. This is the point of the check.';
el('run').disabled = false;
}
el('run').addEventListener('click', tick);
el('reset').addEventListener('click', () => {
ledger = JSON.parse(JSON.stringify(LEDGER_SEED));
paint();
phase('');
el('log').textContent = 'waiting for tick...';
el('hint').textContent = 'Idle. Press Run tick.';
el('run').disabled = false;
});
paint();
</script>
</body>
</html>