File size: 9,826 Bytes
601688c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48eff88
 
44d9a27
601688c
 
 
 
 
 
 
 
 
44d9a27
601688c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44d9a27
601688c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15d2011
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
<!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>