eyeling 1.16.2 → 1.16.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/HANDBOOK.md +4 -0
  2. package/README.md +0 -1
  3. package/examples/ershov-mixed-computation.n3 +106 -0
  4. package/examples/output/ershov-mixed-computation.n3 +15 -0
  5. package/eyeling.js +510 -263
  6. package/lib/cli.js +22 -12
  7. package/lib/engine.js +488 -251
  8. package/package.json +2 -3
  9. package/arctifacts/README.md +0 -59
  10. package/arctifacts/ackermann.html +0 -678
  11. package/arctifacts/auroracare.html +0 -1297
  12. package/arctifacts/bike-trip.html +0 -752
  13. package/arctifacts/binomial-theorem.html +0 -631
  14. package/arctifacts/bmi.html +0 -511
  15. package/arctifacts/building-performance.html +0 -750
  16. package/arctifacts/clinical-care.html +0 -726
  17. package/arctifacts/collatz.html +0 -403
  18. package/arctifacts/complex.html +0 -321
  19. package/arctifacts/control-system.html +0 -482
  20. package/arctifacts/delfour.html +0 -849
  21. package/arctifacts/earthquake-epicenter.html +0 -982
  22. package/arctifacts/eco-route.html +0 -662
  23. package/arctifacts/euclid-infinitude.html +0 -564
  24. package/arctifacts/euler-identity.html +0 -667
  25. package/arctifacts/exoplanet-transit.html +0 -1000
  26. package/arctifacts/faltings-theorem.html +0 -1046
  27. package/arctifacts/fibonacci.html +0 -299
  28. package/arctifacts/fundamental-theorem-arithmetic.html +0 -398
  29. package/arctifacts/godel-numbering.html +0 -743
  30. package/arctifacts/gps-bike.html +0 -759
  31. package/arctifacts/gps-clinical-bench.html +0 -792
  32. package/arctifacts/graph-french.html +0 -449
  33. package/arctifacts/grass-molecular.html +0 -592
  34. package/arctifacts/group-theory.html +0 -740
  35. package/arctifacts/health-info.html +0 -833
  36. package/arctifacts/kaprekar-constant.html +0 -576
  37. package/arctifacts/lee.html +0 -805
  38. package/arctifacts/linked-lists.html +0 -502
  39. package/arctifacts/lldm.html +0 -612
  40. package/arctifacts/matrix-multiplication.html +0 -502
  41. package/arctifacts/matrix.html +0 -651
  42. package/arctifacts/newton-raphson.html +0 -944
  43. package/arctifacts/peano-factorial.html +0 -456
  44. package/arctifacts/pi.html +0 -363
  45. package/arctifacts/polynomial.html +0 -646
  46. package/arctifacts/prime.html +0 -366
  47. package/arctifacts/pythagorean-theorem.html +0 -468
  48. package/arctifacts/rest-path.html +0 -469
  49. package/arctifacts/roots-of-unity.html +0 -363
  50. package/arctifacts/turing.html +0 -409
  51. package/arctifacts/wind-turbines.html +0 -726
@@ -1,792 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>GPS Clinical Bench</title>
7
- <style>
8
- :root {
9
- --bg: #f7fafc;
10
- --panel: #ffffff;
11
- --muted: #475569;
12
- --text: #0b1220;
13
- --accent: #2563eb;
14
- --good: #16a34a;
15
- --bad: #dc2626;
16
- --warn: #d97706;
17
- --chip: #eef2f7;
18
- }
19
- * {
20
- box-sizing: border-box;
21
- }
22
- body {
23
- margin: 0;
24
- font:
25
- 16px/1.5 system-ui,
26
- Segoe UI,
27
- Roboto,
28
- Inter,
29
- Helvetica,
30
- Arial,
31
- sans-serif;
32
- background: var(--bg);
33
- color: var(--text);
34
- }
35
- header {
36
- padding: 24px 20px;
37
- border-bottom: 1px solid #e5e7eb;
38
- background: linear-gradient(180deg, rgba(37, 99, 235, 0.08), transparent);
39
- }
40
- h1 {
41
- margin: 0;
42
- font-size: 24px;
43
- letter-spacing: 0.2px;
44
- }
45
- main {
46
- display: grid;
47
- grid-template-columns: 1fr;
48
- gap: 22px;
49
- padding: 20px;
50
- align-items: start;
51
- }
52
- section {
53
- background: var(--panel);
54
- border: 1px solid #e5e7eb;
55
- border-radius: 16px;
56
- padding: 16px;
57
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.06);
58
- }
59
- h2 {
60
- margin: 0 0 10px 0;
61
- font-size: 18px;
62
- }
63
- .row {
64
- display: flex;
65
- gap: 10px;
66
- flex-wrap: wrap;
67
- }
68
- .grid {
69
- display: grid;
70
- gap: 10px;
71
- }
72
- .pill {
73
- display: inline-flex;
74
- align-items: center;
75
- gap: 8px;
76
- padding: 8px 10px;
77
- border-radius: 999px;
78
- background: #ffffff;
79
- border: 1px solid #e5e7eb;
80
- }
81
- .small {
82
- font-size: 13px;
83
- color: var(--muted);
84
- }
85
- .mono {
86
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
87
- }
88
- .ok {
89
- color: var(--good);
90
- }
91
- .no {
92
- color: var(--bad);
93
- }
94
- .warn {
95
- color: var(--warn);
96
- }
97
- .kvs {
98
- display: grid;
99
- grid-template-columns: 160px 1fr;
100
- gap: 6px 10px;
101
- }
102
- .log {
103
- max-height: 260px;
104
- overflow: auto;
105
- background: #ffffff;
106
- border: 1px solid #e5e7eb;
107
- border-radius: 12px;
108
- padding: 10px;
109
- }
110
- .timeline {
111
- display: flex;
112
- flex-direction: column;
113
- gap: 10px;
114
- }
115
- .card {
116
- background: #ffffff;
117
- border: 1px solid #e5e7eb;
118
- border-radius: 12px;
119
- padding: 12px;
120
- }
121
- .muted {
122
- color: var(--muted);
123
- }
124
- .split {
125
- display: grid;
126
- grid-template-columns: 1fr auto;
127
- gap: 10px;
128
- align-items: center;
129
- }
130
- .btnrow {
131
- display: flex;
132
- gap: 10px;
133
- flex-wrap: wrap;
134
- }
135
- button {
136
- cursor: pointer;
137
- background: var(--accent);
138
- color: #fff;
139
- border: none;
140
- padding: 10px 14px;
141
- border-radius: 12px;
142
- font-weight: 600;
143
- }
144
- button.ghost {
145
- background: #f8fafc;
146
- color: var(--text);
147
- border: 1px solid #dbe2ea;
148
- }
149
- pre.wrap {
150
- white-space: pre-wrap;
151
- overflow-wrap: anywhere;
152
- word-break: break-word;
153
- overflow-x: hidden;
154
- overflow-y: auto;
155
- }
156
- /* New: nicer lists for Reason/Checks */
157
- .list {
158
- margin: 0.25rem 0 0.75rem 1.1rem;
159
- padding: 0;
160
- }
161
- .list li {
162
- margin: 0.1rem 0;
163
- }
164
- .list.compact li {
165
- margin: 0;
166
- }
167
- </style>
168
- </head>
169
- <body>
170
- <header>
171
- <h1>GPS Clinical Bench</h1>
172
- </header>
173
-
174
- <main>
175
- <section id="controls">
176
- <div class="split">
177
- <h2>Run Demo</h2>
178
- <div class="btnrow">
179
- <button id="btn-run">Evaluate</button>
180
- <button class="ghost" id="btn-copy">Copy result JSON</button>
181
- </div>
182
- </div>
183
- </section>
184
-
185
- <section id="decision">
186
- <h2>Decision</h2>
187
- <div class="kvs">
188
- <div class="muted">Answer</div>
189
- <div id="ans">—</div>
190
- <div class="muted">Reason why</div>
191
- <div id="why" class="wrap">Data & policies loaded. Click <em>Evaluate</em>.</div>
192
- <div class="muted">Check (harness)</div>
193
- <div id="check" class="wrap">—</div>
194
- </div>
195
- </section>
196
-
197
- <section id="data">
198
- <h2>Data (JSON)</h2>
199
- <pre class="mono wrap" id="data-json"></pre>
200
- </section>
201
-
202
- <section id="policies">
203
- <h2>Policies (declarative JSON rules)</h2>
204
- <pre class="mono wrap" id="policies-json"></pre>
205
- <h3>Machine Trace (debug)</h3>
206
- <!-- switched to <pre> to ensure one-per-line display -->
207
- <pre class="log mono" id="trace"></pre>
208
- </section>
209
-
210
- <section id="timeline">
211
- <h2>Timeline</h2>
212
- <div class="timeline" id="timeline-items">
213
- <div class="card muted">Run the demo to populate results.</div>
214
- </div>
215
- </section>
216
- </main>
217
-
218
- <script>
219
- /* Surface any runtime errors */
220
- window.onerror = function (msg, src, line, col, err) {
221
- var t = document.getElementById('trace');
222
- if (t) {
223
- t.textContent = 'JS error: ' + msg + ' @ ' + line + ':' + col + (err && err.stack ? '\n' + err.stack : '');
224
- }
225
- };
226
-
227
- /*** DATA (pure JSON) — patient, limits, start/goal, and domain transitions ***/
228
- var Data = {
229
- patient: { id: 'patient_001', gender: 'female', age: 45 },
230
- limits: { maxDays: 180, maxCost: 500, minSuccess: 0.35, minComfort: 0.35, maxStages: 10 },
231
- start: 'state_2',
232
- goal: 'state_6',
233
- actions: [
234
- {
235
- id: 'action:take_pill_Medication_16.',
236
- from: 'state_2',
237
- to: 'state_3',
238
- days: 1,
239
- cost: 69.0,
240
- succ: 0.489,
241
- comf: 0.632,
242
- gender: 'ANY',
243
- ageMin: 18,
244
- ageMax: 99,
245
- },
246
- {
247
- id: 'action:take_pill_Medication_33.',
248
- from: 'state_2',
249
- to: 'state_3',
250
- days: 1,
251
- cost: 57.0,
252
- succ: 0.387,
253
- comf: 0.422,
254
- gender: 'ANY',
255
- ageMin: 18,
256
- ageMax: 99,
257
- },
258
- {
259
- id: 'action:take_pill_Medication_35.',
260
- from: 'state_2',
261
- to: 'state_3',
262
- days: 1,
263
- cost: 81.0,
264
- succ: 0.381,
265
- comf: 0.796,
266
- gender: 'ANY',
267
- ageMin: 18,
268
- ageMax: 99,
269
- },
270
- {
271
- id: 'action:take_pill_Medication_37.',
272
- from: 'state_2',
273
- to: 'state_9',
274
- days: 1,
275
- cost: 53.0,
276
- succ: 0.934,
277
- comf: 0.466,
278
- gender: 'ANY',
279
- ageMin: 18,
280
- ageMax: 99,
281
- },
282
- {
283
- id: 'action:take_pill_Medication_39.',
284
- from: 'state_2',
285
- to: 'state_5',
286
- days: 1,
287
- cost: 2.0,
288
- succ: 0.771,
289
- comf: 0.815,
290
- gender: 'ANY',
291
- ageMin: 18,
292
- ageMax: 99,
293
- },
294
- {
295
- id: 'action:take_pill_Medication_49.',
296
- from: 'state_2',
297
- to: 'state_6',
298
- days: 1,
299
- cost: 44.0,
300
- succ: 0.374,
301
- comf: 0.45,
302
- gender: 'ANY',
303
- ageMin: 18,
304
- ageMax: 99,
305
- },
306
- {
307
- id: 'action:take_pill_Medication_58.',
308
- from: 'state_2',
309
- to: 'state_10',
310
- days: 1,
311
- cost: 87.0,
312
- succ: 0.69,
313
- comf: 0.55,
314
- gender: 'ANY',
315
- ageMin: 18,
316
- ageMax: 99,
317
- },
318
- {
319
- id: 'action:take_pill_Medication_60.',
320
- from: 'state_2',
321
- to: 'state_5',
322
- days: 1,
323
- cost: 12.0,
324
- succ: 0.723,
325
- comf: 0.85,
326
- gender: 'ANY',
327
- ageMin: 18,
328
- ageMax: 99,
329
- },
330
- ],
331
- };
332
-
333
- /*** ARC-style declarative rules (pure JSON) ***/
334
- var ARC_RULES = [
335
- {
336
- id: 'R1-ApplicableFromStart',
337
- if: [
338
- { pred: 'start', s: 'Req', o: '?S' },
339
- { pred: 'edge', s: '?S', o: '?A' },
340
- ],
341
- then: [{ pred: 'applicable', s: 'Req', o: '?A' }],
342
- explain:
343
- 'Any transition whose source equals the start state is applicable (patient filters are checked in JS).',
344
- },
345
- {
346
- id: 'R2-ReachesGoalInOneStep',
347
- if: [
348
- { pred: 'start', s: 'Req', o: '?S' },
349
- { pred: 'goal', s: 'Req', o: '?G' },
350
- { pred: 'edgeTo', s: '?A', o: '?T' },
351
- { pred: 'equals', s: '?T', o: '?G' },
352
- ],
353
- then: [{ pred: 'reachesGoalOne', s: 'Req', o: '?A' }],
354
- explain: 'An applicable transition reaches the goal in one step if its target equals the goal state.',
355
- },
356
- ];
357
-
358
- /*** Pretty printer for rules (inline IF/THEN atoms; per-line; wrapped) ***/
359
- function formatInlineObject(o) {
360
- var order = ['pred', 's', 'o', 'not'];
361
- var keys = Object.keys(o).sort(function (a, b) {
362
- var ai = order.indexOf(a),
363
- bi = order.indexOf(b);
364
- if (ai === -1 && bi === -1) return a < b ? -1 : a > b ? 1 : 0;
365
- if (ai === -1) return 1;
366
- if (bi === -1) return -1;
367
- return ai - bi;
368
- });
369
- var parts = keys.map(function (k) {
370
- var v = o[k],
371
- vs = typeof v === 'string' ? JSON.stringify(v) : typeof v === 'boolean' ? String(v) : JSON.stringify(v);
372
- return JSON.stringify(k) + ':' + vs;
373
- });
374
- return '{' + parts.join(',') + '}';
375
- }
376
- function formatRules(rules) {
377
- var out = ['['];
378
- for (var i = 0; i < rules.length; i++) {
379
- var r = rules[i];
380
- out.push(' {');
381
- out.push(' "id": ' + JSON.stringify(r.id) + ',');
382
- out.push(' "if": [ ' + (r.if || []).map(formatInlineObject).join(', ') + ' ],');
383
- var thenLine = ' "then": [ ' + (r.then || []).map(formatInlineObject).join(', ') + ' ]';
384
- if (r.explain !== undefined) thenLine += ',';
385
- out.push(thenLine);
386
- if (r.explain !== undefined) out.push(' "explain": ' + JSON.stringify(r.explain));
387
- out.push(' }' + (i < rules.length - 1 ? ',' : ''));
388
- }
389
- out.push(']');
390
- return out.join('\n');
391
- }
392
-
393
- /*** Minimal ARC forward-chainer (facts are {pred,s,o}) ***/
394
- function isVar(x) {
395
- return typeof x === 'string' && x[0] === '?';
396
- }
397
- function subst(term, theta) {
398
- var t = { pred: term.pred, s: term.s, o: term.o };
399
- if (isVar(t.s) && theta[t.s] !== undefined) t.s = theta[t.s];
400
- if (isVar(t.o) && theta[t.o] !== undefined) t.o = theta[t.o];
401
- return t;
402
- }
403
- function unifyAtom(pattern, fact, theta) {
404
- if (pattern.pred !== fact.pred) return null;
405
- var s = Object.assign({}, theta || {});
406
- function bind(pv, fv) {
407
- if (isVar(pv)) {
408
- if (s[pv] === undefined) {
409
- s[pv] = fv;
410
- return true;
411
- }
412
- return s[pv] === fv;
413
- }
414
- return pv === fv;
415
- }
416
- if (!bind(pattern.s, fact.s)) return null;
417
- if (!bind(pattern.o, fact.o)) return null;
418
- return s;
419
- }
420
- function matchBody(body, facts) {
421
- function extend(i, theta, acc) {
422
- if (i === body.length) {
423
- acc.push(theta);
424
- return;
425
- }
426
- var atom = body[i];
427
- if (atom.not) {
428
- var any = false;
429
- for (var k = 0; k < facts.length; k++) {
430
- if (unifyAtom(atom, facts[k], theta)) {
431
- any = true;
432
- break;
433
- }
434
- }
435
- if (!any) extend(i + 1, theta, acc);
436
- return;
437
- }
438
- for (var k = 0; k < facts.length; k++) {
439
- var t2 = unifyAtom(atom, facts[k], theta);
440
- if (t2) extend(i + 1, t2, acc);
441
- }
442
- }
443
- var out = [];
444
- extend(0, {}, out);
445
- return out;
446
- }
447
- function factKey(f) {
448
- return f.pred + '|' + JSON.stringify(f.s) + '|' + JSON.stringify(f.o);
449
- }
450
- function containsFact(facts, f) {
451
- var key = factKey(f);
452
- for (var i = 0; i < facts.length; i++) {
453
- if (factKey(facts[i]) === key) return true;
454
- }
455
- return false;
456
- }
457
- function derive(facts, rules) {
458
- var added = true,
459
- trace = [];
460
- while (added) {
461
- added = false;
462
- for (var r = 0; r < rules.length; r++) {
463
- var rule = rules[r];
464
- var thetas = matchBody(rule.if, facts);
465
- for (var t = 0; t < thetas.length; t++) {
466
- for (var j = 0; j < rule.then.length; j++) {
467
- var f = subst(rule.then[j], thetas[t]);
468
- if (!containsFact(facts, f)) {
469
- facts.push(f);
470
- trace.push('[' + rule.id + '] ' + JSON.stringify(f));
471
- added = true;
472
- }
473
- }
474
- }
475
- }
476
- }
477
- return { facts: facts, trace: trace };
478
- }
479
-
480
- /*** Build base facts from Data (edges and labels) ***/
481
- function factsFromData(d) {
482
- var F = [];
483
- function add(pred, s, o) {
484
- F.push({ pred: pred, s: s, o: o });
485
- }
486
- add('start', 'Req', d.start);
487
- add('goal', 'Req', d.goal);
488
- // Encode edges as facts the rules can see (source->action & action->target)
489
- d.actions.forEach(function (a) {
490
- add('edge', '' + a.from, a.id); // edge(from, actionId)
491
- add('edgeTo', a.id, '' + a.to); // edgeTo(actionId, to)
492
- });
493
- // Provide equality helper so R2 can check target==goal
494
- var states = new Set();
495
- states.add(d.start);
496
- states.add(d.goal);
497
- d.actions.forEach(function (a) {
498
- states.add(a.from);
499
- states.add(a.to);
500
- });
501
- states.forEach(function (s) {
502
- add('equals', s, s);
503
- });
504
- return F;
505
- }
506
-
507
- /*** Domain helpers ***/
508
- function fmtEuro(x) {
509
- return '€' + x.toFixed(2);
510
- }
511
- function applicableForPatient(a, patient) {
512
- var genderOk =
513
- a.gender === 'ANY' || a.gender === patient.gender.toUpperCase() || a.gender.toLowerCase() === patient.gender;
514
- var ageOk = patient.age >= a.ageMin && patient.age <= a.ageMax;
515
- return genderOk && ageOk;
516
- }
517
- function withinGlobalLimits(a, limits) {
518
- return (
519
- a.days <= limits.maxDays &&
520
- a.cost <= limits.maxCost &&
521
- a.succ >= limits.minSuccess &&
522
- a.comf >= limits.minComfort
523
- );
524
- }
525
- function escapeHTML(s) {
526
- return String(s).replace(/[&<>"']/g, function (c) {
527
- return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
528
- });
529
- }
530
-
531
- /*** Main reasoning: list applicable, find one-step goal transitions, build messages ***/
532
- function runOnce() {
533
- try {
534
- // 1) Show Data & Policies
535
- document.getElementById('data-json').textContent = JSON.stringify(Data, null, 2);
536
- document.getElementById('policies-json').textContent = formatRules(ARC_RULES);
537
-
538
- // 2) Build facts & run ARC rules (for machine trace)
539
- var baseFacts = factsFromData(Data);
540
- var closure = derive(baseFacts.slice(), ARC_RULES);
541
-
542
- // 3) JS specialized reasoning
543
- var start = Data.start,
544
- goal = Data.goal,
545
- lim = Data.limits,
546
- pat = Data.patient;
547
-
548
- var applicable = Data.actions.filter(function (a) {
549
- return a.from === start && applicableForPatient(a, pat);
550
- });
551
- function bullet(a) {
552
- return (
553
- a.id +
554
- ' : ' +
555
- start +
556
- ' \u2192 care:' +
557
- a.to +
558
- '. (+1d, +' +
559
- fmtEuro(a.cost) +
560
- ', ×succ ' +
561
- a.succ.toFixed(3) +
562
- ', ×comf ' +
563
- a.comf.toFixed(3) +
564
- ')'
565
- );
566
- }
567
- var bullets = applicable.map(bullet);
568
-
569
- var oneStepGoals = applicable.filter(function (a) {
570
- return a.to === goal && withinGlobalLimits(a, lim);
571
- });
572
-
573
- // Reason: global limits + applicable list (pretty multi-line)
574
- var whyBox = document.getElementById('why');
575
- var limitsHTML =
576
- '<div><strong>Global limits:</strong></div>' +
577
- '<ul class="list compact mono small">' +
578
- '<li>duration ≤ ' +
579
- lim.maxDays +
580
- ' days</li>' +
581
- '<li>cost ≤ ' +
582
- fmtEuro(lim.maxCost) +
583
- '</li>' +
584
- '<li>success ≥ ' +
585
- lim.minSuccess.toFixed(2) +
586
- '</li>' +
587
- '<li>comfort ≥ ' +
588
- lim.minComfort.toFixed(2) +
589
- '</li>' +
590
- '<li>stagecount ≤ ' +
591
- lim.maxStages +
592
- '</li>' +
593
- '</ul>';
594
-
595
- var applicableHTML =
596
- '<div><strong>From the start state ' +
597
- escapeHTML(start) +
598
- ', applicable rules for this patient: ' +
599
- applicable.length +
600
- '</strong></div>' +
601
- '<ul class="list mono">' +
602
- bullets
603
- .map(function (t) {
604
- return '<li>' + escapeHTML(t) + '</li>';
605
- })
606
- .join('') +
607
- '</ul>';
608
-
609
- // Explanation lines
610
- var otherStates = Array.from(
611
- new Set(
612
- applicable
613
- .map(function (a) {
614
- return a.to;
615
- })
616
- .filter(function (s) {
617
- return s !== goal;
618
- }),
619
- ),
620
- ).slice(0, 3);
621
- var explHTML;
622
- if (oneStepGoals.length === 1) {
623
- var a = oneStepGoals[0];
624
- explHTML =
625
- '<div><strong>Explanation:</strong></div>' +
626
- '<ul class="list">' +
627
- '<li>The only applicable transition that reaches the goal in one step is<br>' +
628
- 'action ‘' +
629
- escapeHTML(a.id.replace(/\.$/, '')) +
630
- '’: ' +
631
- escapeHTML(start) +
632
- ' → ' +
633
- escapeHTML(goal) +
634
- '.</li>' +
635
- '<li>Other applicable first steps (e.g. to ' +
636
- escapeHTML(otherStates.join(', ')) +
637
- ') do not lead to ' +
638
- escapeHTML(goal) +
639
- ' under the gender/age filters and global limits in subsequent steps.</li>' +
640
- '</ul>';
641
- } else if (oneStepGoals.length > 1) {
642
- explHTML =
643
- '<div><strong>Explanation:</strong></div>' +
644
- '<ul class="list"><li>Multiple one-step transitions reach the goal. Choose by cost/success/comfort trade-off.</li></ul>';
645
- } else {
646
- explHTML =
647
- '<div><strong>Explanation:</strong></div>' +
648
- '<ul class="list"><li>No one-step transition reaches the goal under the global limits.</li></ul>';
649
- }
650
- whyBox.innerHTML = limitsHTML + applicableHTML + explHTML;
651
-
652
- // Checks (harness) — pretty bullet list
653
- var checksBox = document.getElementById('check');
654
- var checksItems = [];
655
- if (oneStepGoals.length === 1) {
656
- var g = oneStepGoals[0];
657
- checksItems.push(
658
- "Unique solution confirmed: ['" +
659
- g.id +
660
- "'] (succ=" +
661
- g.succ.toFixed(3) +
662
- ', cost=' +
663
- fmtEuro(g.cost) +
664
- ', dur=' +
665
- g.days +
666
- 'd, comf=' +
667
- g.comf.toFixed(3) +
668
- '). ✓',
669
- );
670
- checksItems.push('Harness complete: reasoning, applicability, limits, and optimality verified. ✓');
671
- } else if (oneStepGoals.length > 1) {
672
- checksItems.push('Multiple valid solutions found; harness notes non-uniqueness. ✓');
673
- } else {
674
- checksItems.push('No valid one-step solution within limits; harness notes infeasibility. ✓');
675
- }
676
- checksBox.innerHTML =
677
- '<ul class="list mono">' +
678
- checksItems
679
- .map(function (c) {
680
- return '<li>' + escapeHTML(c) + '</li>';
681
- })
682
- .join('') +
683
- '</ul>';
684
-
685
- // Decision
686
- var decision = oneStepGoals.length > 0 ? 'Allowed' : 'Denied';
687
- document.getElementById('ans').innerHTML =
688
- decision === 'Allowed' ? '<strong class="ok">✅ Allowed</strong>' : '<strong class="no">⛔ Denied</strong>';
689
-
690
- // Trace + Timeline + Stash
691
- document.getElementById('trace').textContent = closure.trace.join('\n');
692
- pushTimeline(
693
- { start: start, goal: goal },
694
- {
695
- answer: decision,
696
- reason: oneStepGoals.length ? 'Chosen: ' + oneStepGoals[0].id : 'No one-step solution',
697
- },
698
- );
699
- window.__lastResult = {
700
- data: Data,
701
- facts: baseFacts,
702
- derived: closure.facts,
703
- trace: closure.trace,
704
- start: start,
705
- goal: goal,
706
- applicable: applicable,
707
- oneStepGoals: oneStepGoals,
708
- decision: decision,
709
- ts: new Date().toISOString(),
710
- };
711
- } catch (e) {
712
- var t = document.getElementById('trace');
713
- if (t) t.textContent = 'Evaluate error: ' + ((e && e.stack) || e);
714
- }
715
- }
716
-
717
- /*** Timeline helper ***/
718
- function el(tag, attrs, children) {
719
- attrs = attrs || {};
720
- children = children || [];
721
- var n = document.createElement(tag);
722
- for (var k in attrs) {
723
- if (!attrs.hasOwnProperty(k)) continue;
724
- var v = attrs[k];
725
- if (k === 'class') n.className = v;
726
- else if (k === 'html') n.innerHTML = v;
727
- else if (k.indexOf('on') === 0) n.addEventListener(k.slice(2), v);
728
- else n.setAttribute(k, v);
729
- }
730
- for (var i = 0; i < children.length; i++) n.append(children[i]);
731
- return n;
732
- }
733
- function pushTimeline(meta, res) {
734
- var box = document.getElementById('timeline-items');
735
- var card = el('div', { class: 'card' });
736
- var when = new Date().toLocaleString();
737
- card.innerHTML =
738
- '<div class="row" style="justify-content:space-between"><strong>' +
739
- (res.answer === 'Allowed' ? '✅ Allowed' : '⛔ Denied') +
740
- '</strong><span class="muted small">' +
741
- when +
742
- '</span></div>' +
743
- '<div class="small muted">start=<span class="mono">' +
744
- meta.start +
745
- '</span> • goal=<span class="mono">' +
746
- meta.goal +
747
- '</span></div>' +
748
- '<div class="small">' +
749
- (res.reason || '') +
750
- '</div>';
751
- box.prepend(card);
752
- }
753
-
754
- /*** Boot ***/
755
- function boot() {
756
- document.getElementById('data-json').textContent = JSON.stringify(Data, null, 2);
757
- document.getElementById('policies-json').textContent = formatRules(ARC_RULES);
758
- document.getElementById('btn-run').addEventListener('click', runOnce);
759
- document.getElementById('btn-copy').addEventListener('click', function () {
760
- try {
761
- var payload = JSON.stringify(window.__lastResult || { error: 'no-result' }, null, 2);
762
- navigator.clipboard.writeText(payload);
763
- var b = document.getElementById('btn-copy');
764
- var old = b.textContent;
765
- b.textContent = 'Copied!';
766
- setTimeout(function () {
767
- b.textContent = old;
768
- }, 1000);
769
- } catch (e) {
770
- alert('Copy failed: ' + e.message);
771
- }
772
- });
773
- }
774
- window.addEventListener('DOMContentLoaded', function () {
775
- try {
776
- boot();
777
- } catch (e) {
778
- var t = document.getElementById('trace');
779
- if (t) t.textContent = 'Boot error: ' + ((e && e.stack) || e);
780
- }
781
- });
782
- if (document.readyState !== 'loading') {
783
- try {
784
- boot();
785
- } catch (e) {
786
- var t = document.getElementById('trace');
787
- if (t) t.textContent = 'Boot error: ' + ((e && e.stack) || e);
788
- }
789
- }
790
- </script>
791
- </body>
792
- </html>