File size: 75,298 Bytes
ec957e9 5518015 ec957e9 | 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 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 | <!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Consent / Revocation Propagation Simulator v0.0.4</title>
<meta name="description" content="A HIR/OAM living-boundary simulator for testing whether consent survives handoff and whether revocation reaches downstream branches."/>
<style>
:root{
--bg:#070a0f; --panel:#111827; --panel2:#0b111b; --ink:#f7f0df; --muted:#aeb8c8;
--gold:#f0c96a; --blue:#80b7ff; --green:#7adf98; --orange:#ffb35d; --red:#ff6b6b;
--purple:#c9a7ff; --cyan:#58e6d9; --line:#2d3a50; --soft:rgba(255,255,255,.045);
}
*{box-sizing:border-box}
body{
margin:0;
font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
color:var(--ink);
background:
radial-gradient(circle at 12% 8%,rgba(240,201,106,.16),transparent 30%),
radial-gradient(circle at 84% 8%,rgba(128,183,255,.13),transparent 34%),
radial-gradient(circle at 50% 98%,rgba(88,230,217,.08),transparent 42%),
linear-gradient(180deg,#070a0f 0%,#0d121b 54%,#07090d 100%);
line-height:1.55;
}
.wrap{width:min(1220px,calc(100% - 32px));margin:0 auto}
header{padding:56px 0 28px;border-bottom:1px solid var(--line)}
section{padding:26px 0}
.eyebrow{font-size:12px;text-transform:uppercase;letter-spacing:.16em;color:var(--gold);font-weight:950}
h1{font-size:clamp(38px,7vw,78px);letter-spacing:-.06em;line-height:.94;margin:12px 0}
h2{font-size:clamp(26px,4vw,42px);letter-spacing:-.04em;line-height:1;margin:0 0 12px}
h3{margin:18px 0 8px}
.subtitle{font-size:clamp(18px,2.5vw,28px);color:#dce6f4;max-width:1040px}
.card{
background:linear-gradient(135deg,rgba(17,24,39,.96),rgba(14,21,34,.92));
border:1px solid var(--line); border-radius:24px; padding:22px; box-shadow:0 18px 45px rgba(0,0,0,.35)
}
.hero-grid{display:grid;grid-template-columns:1.08fr .92fr;gap:18px;margin-top:24px}
.grid{display:grid;grid-template-columns:.92fr 1.08fr;gap:18px;align-items:start}
.dual{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.muted{color:var(--muted)}.gold{color:var(--gold)}.green{color:var(--green)}.red{color:var(--red)}.blue{color:var(--blue)}.orange{color:var(--orange)}
.equation,textarea{
font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
background:#070a0f; color:#f4d88e; border:1px solid #39475f; border-radius:16px; padding:14px; width:100%;
}
textarea{min-height:90px;color:#dfe7f3;resize:vertical}
.pillrow{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
.pill,.btn{
border:1px solid #3a4a63;background:#101722;color:#dce6f4;border-radius:999px;
padding:8px 11px;font-size:12px;font-weight:850;cursor:pointer;text-decoration:none;
}
.btn.primary{border-color:rgba(240,201,106,.72);color:var(--gold)}
.btn:hover,.pill:hover{border-color:var(--gold)}
select{
width:100%;padding:12px;border-radius:14px;background:#080c13;color:var(--ink);border:1px solid #334057
}
.control{margin:13px 0}
.control label{display:flex;justify-content:space-between;font-weight:780;color:#e7edf7}
input[type=range]{width:100%;accent-color:var(--gold)}
.toggle{display:flex;align-items:center;justify-content:space-between;gap:14px;background:#0b111b;border:1px solid #31405a;border-radius:16px;padding:12px 14px;margin:10px 0}
.toggle span{font-weight:820}.toggle small{display:block;color:var(--muted);font-weight:500}
input[type=checkbox]{width:22px;height:22px;accent-color:var(--gold)}
.state-label{font-size:12px;text-transform:uppercase;letter-spacing:.14em;color:var(--muted);font-weight:900}
.state{font-size:clamp(27px,4vw,46px);line-height:.94;letter-spacing:-.05em;font-weight:950;margin:7px 0 10px}
.consent .state{color:var(--blue)} .revocation .state{color:var(--gold)}
.score-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-top:12px}
.score{background:var(--soft);border:1px solid rgba(255,255,255,.09);border-radius:16px;padding:12px}
.score .label{font-size:10px;text-transform:uppercase;letter-spacing:.1em;color:var(--muted);font-weight:900}
.score .value{font-size:27px;font-weight:950;line-height:1;margin-top:5px}
.readbox{border-left:4px solid var(--gold);padding-left:14px;margin-top:14px;color:#dfe7f3}
.path{
position:relative;overflow:hidden;
background:linear-gradient(180deg,rgba(255,255,255,.045),rgba(255,255,255,.025));
border:1px solid rgba(255,255,255,.09);border-radius:22px;padding:18px
}
.path:before{
content:"";position:absolute;inset:auto -20% 0 -20%;height:2px;
background:linear-gradient(90deg,transparent,var(--gold),transparent);opacity:.35
}
.consent{border-color:rgba(128,183,255,.35)}
.revocation{border-color:rgba(240,201,106,.35)}
.flags{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
.flag{border:1px solid #44556f;background:#0c121c;border-radius:999px;padding:7px 10px;font-size:12px;font-weight:850}
.hot{border-color:rgba(255,107,107,.45);color:#ffd0d0;background:rgba(255,107,107,.10)}
.warn{border-color:rgba(255,179,93,.45);color:#ffe0bd;background:rgba(255,179,93,.10)}
.good{border-color:rgba(122,223,152,.45);color:#c9ffd8;background:rgba(122,223,152,.10)}
.info{border-color:rgba(128,183,255,.45);color:#cfe4ff;background:rgba(128,183,255,.10)}
.compare{display:grid;grid-template-columns:1fr auto 1fr;gap:12px;align-items:center}
.arrow{font-size:42px;color:var(--gold);font-weight:950;text-align:center}
.route-card{background:#0b111b;border:1px solid var(--line);border-radius:18px;padding:16px}
.route-card b{color:var(--gold)}.route-card p{color:var(--muted);font-size:13px}
.taxonomy{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
footer{border-top:1px solid var(--line);padding:24px 0 40px;color:var(--muted)}
@media(max-width:900px){.hero-grid,.grid,.dual,.compare,.taxonomy,.score-grid{grid-template-columns:1fr}.arrow{transform:rotate(90deg)}}
details.logicBox{
border:1px solid var(--line);
border-radius:20px;
background:rgba(255,255,255,.035);
padding:14px 16px;
margin:14px 0;
}
details.logicBox summary{
cursor:pointer;
color:var(--gold);
font-weight:950;
letter-spacing:-.01em;
}
.logicGrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-top:12px}
.logicMini{
border:1px solid rgba(255,255,255,.09);
background:#0b111b;
border-radius:16px;
padding:12px;
color:#dfe7f3;
}
.logicMini b{color:var(--gold)}
.routeProofTable{width:100%;border-collapse:collapse;margin-top:14px;font-size:13px;overflow:hidden;border-radius:14px}
.routeProofTable th,.routeProofTable td{border:1px solid var(--line);padding:10px;text-align:left;vertical-align:top}
.routeProofTable th{background:rgba(240,201,106,.10);color:var(--gold)}
.routeProofTable td{background:rgba(255,255,255,.025);color:#dfe7f3}
@media(max-width:900px){.logicGrid{grid-template-columns:1fr}.routeProofTable{font-size:12px}}
.sensitivityBox{
margin-top:14px;
border:1px solid rgba(240,201,106,.24);
background:linear-gradient(135deg,rgba(240,201,106,.06),rgba(128,183,255,.04));
border-radius:20px;
padding:14px;
}
.sensitivityGrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin-top:10px}
.senseCard{
background:#0b111b;
border:1px solid rgba(255,255,255,.09);
border-radius:16px;
padding:12px;
}
.senseCard .miniLabel{font-size:10px;text-transform:uppercase;letter-spacing:.10em;color:var(--muted);font-weight:900}
.senseCard .miniValue{font-size:20px;font-weight:950;margin-top:4px}
.senseStable{color:var(--green)}
.senseFragile{color:var(--orange)}
.senseHot{color:var(--red)}
.senseLocked{color:var(--gold)}
@media(max-width:900px){.sensitivityGrid{grid-template-columns:1fr}}
.driverTrace{
margin-top:12px;
border:1px solid rgba(255,255,255,.09);
background:#0b111b;
border-radius:16px;
padding:12px;
}
.driverTrace h4{
margin:0 0 8px;
font-size:13px;
color:var(--gold);
letter-spacing:.02em;
}
.driverCols{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.driverList{
margin:0;
padding-left:18px;
color:#dfe7f3;
font-size:12px;
line-height:1.45;
}
.driverList li{margin:4px 0}
.driverNone{color:var(--muted);font-size:12px;margin:0}
@media(max-width:900px){.driverCols{grid-template-columns:1fr}}
</style>
</head>
<body>
<header>
<div class="wrap">
<div class="eyebrow">Consent / Revocation Propagation Simulator v0.0.4 — Sensitivity Drivers + Flip Trace Patch</div>
<h1>Did the boundary survive the handoff?</h1>
<p class="subtitle">Consent is not a checkbox. It is a living boundary that has to travel. Revocation is not erasure. It is a boundary delta that must reach every affected branch.</p>
<div class="hero-grid">
<div class="card">
<h2>A consent boundary is only real if revocation can reach the branches.</h2>
<p>Most systems ask: did the user consent?</p>
<p>This simulator asks: consent to what, for whom, for how long, under what scope, with what revocation path, and can that revocation reach every branch?</p>
<p><b class="gold">This simulator does not provide legal advice, HIPAA compliance certification, privacy-law analysis, or clinical authorization.</b> It maps whether a consent boundary remains structurally intact under handoff, downstream reuse, retention, and revocation pressure.</p>
</div>
<div class="card">
<h2>Dual boundary equations</h2>
<div class="equation">S_consent = A_authorization × B_scope − P_revocation_drift</div>
<div class="equation">S_revocation = R_notice × C_chain × E_enforcement − P_residue</div>
<p class="muted">The first path tests whether consent carried with scope intact. The second path tests whether a revocation or scope change can propagate through downstream branches.</p><p class="muted"><b>v0.0.4 patch:</b> sensitivity drivers, flip traces, and top route-change contributors. Route logic, thresholds, presets, and shareable state unchanged.</p>
</div>
</div>
</div>
</header>
<main>
<section>
<div class="wrap card">
<h2>Explain this to me as…</h2>
<div class="pillrow">
<button class="btn primary" data-mode="plain">Plain words</button>
<button class="btn" data-mode="patient">Patient / EHR</button>
<button class="btn" data-mode="platform">Platform / data reuse</button>
<button class="btn" data-mode="agent">AI agent</button>
<button class="btn" data-mode="system">System map</button>
</div>
<p id="translation" class="readbox">This tool checks whether a consent boundary stayed clear, scoped, current, revocable, and enforceable as it moved through downstream systems.</p>
</div>
</section>
<section>
<div class="wrap card">
<h2>Consent event → handoff → downstream branches</h2>
<div class="compare">
<div>
<h3>Original Consent / Boundary</h3>
<textarea id="originalConsent">Consent is granted for a specific purpose, recipient, duration, data/action boundary, and visible revocation path.</textarea>
</div>
<div class="arrow">→</div>
<div>
<h3>Downstream Use / Revocation Event</h3>
<textarea id="downstreamEvent">The boundary may travel cleanly, drift into a new scope, be partially revoked, or require downstream quarantine until branches confirm compliance.</textarea>
</div>
</div>
<p class="muted">v0.0.1 does not automatically analyze text. These fields preserve scenario context for the export report while sliders model boundary and propagation conditions.</p>
</div>
</section>
<section>
<div class="wrap card">
<h2>Simulator</h2>
<p class="muted"><b>v0.0.1 route note:</b> repair, revocation, quarantine, and human review override ordinary consent-active routing.</p>
<div class="grid">
<div>
<label for="preset"><b>Scenario preset</b></label>
<select id="preset"></select>
<h3>Authorization / consent side</h3>
<div class="control"><label>Explicit Authorization <span id="explicitAuthorizationVal"></span></label><input id="explicitAuthorization" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Informed Understanding <span id="informedUnderstandingVal"></span></label><input id="informedUnderstanding" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Current / Not Expired <span id="currentConsentVal"></span></label><input id="currentConsent" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Identity / Authority Match <span id="identityAuthorityVal"></span></label><input id="identityAuthority" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Consent Record Available <span id="consentRecordVal"></span></label><input id="consentRecord" type="range" min="0" max="1" step="0.01"></div>
<h3>Scope side</h3>
<div class="control"><label>Purpose Clarity <span id="purposeClarityVal"></span></label><input id="purposeClarity" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Recipient Clarity <span id="recipientClarityVal"></span></label><input id="recipientClarity" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Data / Action Boundary <span id="dataActionBoundaryVal"></span></label><input id="dataActionBoundary" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Duration Clarity <span id="durationClarityVal"></span></label><input id="durationClarity" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Revocation Path Visible <span id="revocationPathVisibleVal"></span></label><input id="revocationPathVisible" type="range" min="0" max="1" step="0.01"></div>
<h3>Propagation pressure side</h3>
<div class="control"><label>Downstream Branch Count <span id="downstreamBranchCountVal"></span></label><input id="downstreamBranchCount" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Transfer Opacity <span id="transferOpacityVal"></span></label><input id="transferOpacity" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Retention Friction <span id="retentionFrictionVal"></span></label><input id="retentionFriction" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Automation Lag <span id="automationLagVal"></span></label><input id="automationLag" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Institutional / Commercial Incentive <span id="institutionalIncentiveVal"></span></label><input id="institutionalIncentive" type="range" min="0" max="1" step="0.01"></div>
<h3>Revocation propagation side</h3>
<div class="control"><label>Revocation Notice Clarity <span id="revocationNoticeVal"></span></label><input id="revocationNotice" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Downstream Chain Visibility <span id="chainVisibilityVal"></span></label><input id="chainVisibility" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Enforcement Ability <span id="enforcementAbilityVal"></span></label><input id="enforcementAbility" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Confirmation Returned <span id="confirmationReturnedVal"></span></label><input id="confirmationReturned" type="range" min="0" max="1" step="0.01"></div>
<div class="control"><label>Residual Copy / Cache Risk <span id="residualCopyRiskVal"></span></label><input id="residualCopyRisk" type="range" min="0" max="1" step="0.01"></div>
<h3>Route gates</h3>
<label class="toggle"><span>Consent Currently Granted<small>Authorization exists right now for the evaluated scope.</small></span><input id="consentGranted" type="checkbox"></label>
<label class="toggle"><span>Partial Revocation Present<small>The person narrowed consent rather than fully revoking it.</small></span><input id="partialRevocation" type="checkbox"></label>
<label class="toggle"><span>Full Revocation Present<small>Consent has been withdrawn for the affected scope.</small></span><input id="fullRevocation" type="checkbox"></label>
<label class="toggle"><span>Downstream Sharing Occurred<small>Consent/data/action moved beyond the first receiver.</small></span><input id="downstreamSharing" type="checkbox"></label>
<label class="toggle"><span>Prior Consent Harm Present<small>A prior boundary was violated, erased, ignored, or misrepresented.</small></span><input id="priorConsentHarm" type="checkbox"></label>
<label class="toggle"><span>Sensitive / High-Stakes Context<small>Health, education, family support, safety, identity, money, legal, or similarly high-impact context.</small></span><input id="sensitiveContext" type="checkbox"></label>
</div>
<div>
<div class="dual">
<div class="path consent">
<div class="state-label">Consent Carry State<br><small style="font-size:13px;opacity:.7">scope survival / handoff legitimacy</small></div>
<div id="consentState" class="state">BOUNDED CONSENT</div>
<p id="consentMeaning" class="muted"></p>
<div class="score-grid">
<div class="score"><div class="label">Authorization Support</div><div id="authorizationSupport" class="value"></div></div>
<div class="score"><div class="label">Scope Support</div><div id="scopeSupport" class="value"></div></div>
<div class="score"><div class="label">Consent Stability</div><div id="consentStability" class="value"></div></div>
<div class="score"><div class="label">Drift Pressure</div><div id="revocationDrift" class="value"></div></div>
</div>
<div class="readbox"><b>Read:</b><p id="consentRead"></p><b>Why this route?</b><p id="consentWhy"></p><b>Action:</b><p id="consentAction"></p></div>
<h3>Consent Flags</h3><div id="consentFlags" class="flags"></div>
</div>
<div class="path revocation">
<div class="state-label">Revocation Propagation State<br><small style="font-size:13px;opacity:.7">boundary delta / downstream enforcement</small></div>
<div id="revocationState" class="state">REVOCATION PROPAGATING</div>
<p id="revocationMeaning" class="muted"></p>
<div class="score-grid">
<div class="score"><div class="label">Revocation Support</div><div id="revocationSupport" class="value"></div></div>
<div class="score"><div class="label">Residue Pressure</div><div id="residuePressure" class="value"></div></div>
<div class="score"><div class="label">Propagation Stability</div><div id="revocationStability" class="value"></div></div>
<div class="score"><div class="label">Boundary Delta Strength</div><div id="boundaryDeltaStrength" class="value"></div></div>
</div>
<div class="readbox"><b>Read:</b><p id="revocationRead"></p><b>Why this route?</b><p id="revocationWhy"></p><b>Action:</b><p id="revocationAction"></p></div>
<h3>Revocation Flags</h3><div id="revocationFlags" class="flags"></div>
</div>
</div>
<div class="pillrow">
<button id="exportReport" class="btn primary">Generate boundary report</button>
<button id="copyReport" class="btn">Copy report</button>
<button id="copyScenarioLink" class="btn">Copy scenario link</button>
<button id="runSensitivity" class="btn">Test route stability ±10%</button>
</div>
<div id="sensitivityBox" class="sensitivityBox">
<b class="gold">Route stability sensitivity</b>
<p class="muted" id="sensitivitySummary">Click “Test route stability ±10%” to see whether the current routes are stable, threshold-adjacent, fragile, or override-locked under small slider perturbations.</p>
<div class="sensitivityGrid">
<div class="senseCard"><div class="miniLabel">Consent Route</div><div id="senseConsentRoute" class="miniValue">—</div></div>
<div class="senseCard"><div class="miniLabel">Revocation Route</div><div id="senseRevocationRoute" class="miniValue">—</div></div>
<div class="senseCard"><div class="miniLabel">Overall Stability</div><div id="senseOverall" class="miniValue">—</div></div>
</div>
<div class="driverTrace">
<h4>Top sensitivity drivers / flip trace</h4>
<div class="driverCols">
<div>
<b class="blue">Consent route flips</b>
<ol id="consentDriverList" class="driverList"><li>No sensitivity run yet.</li></ol>
</div>
<div>
<b class="gold">Revocation route flips</b>
<ol id="revocationDriverList" class="driverList"><li>No sensitivity run yet.</li></ol>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<section><div class="wrap card"><h2>Exportable consent-boundary report</h2><textarea id="report" placeholder="Click Generate boundary report..."></textarea></div></section>
<section><div class="wrap card">
<h2>How the model works</h2>
<div class="equation">S_consent = A_authorization × B_scope − P_revocation_drift</div>
<div class="equation">S_revocation = R_notice × C_chain × E_enforcement − P_residue</div>
<p class="readbox">
Consent granted is not enough. Consent carried is the real test.<br><br>
Revocation received is not enough. Revocation propagated is the real test.<br><br>
<b>Revocation propagates as a boundary delta, not erasure.</b> History and audit spine remain preserved, but downstream authorization is downgraded, stopped, quarantined, or re-proofed according to the affected scope.
</p>
<p class="muted" style="margin-top:16px;">
<b>Where:</b><br>
A_authorization = explicit authorization, informed understanding, currency, identity/authority match, and consent record availability.<br>
B_scope = purpose clarity, recipient clarity, data/action boundary, duration clarity, and revocation-path visibility.<br>
P_revocation_drift = downstream branch count, transfer opacity, retention friction, automation lag, and institutional/commercial incentive.<br>
R_notice = revocation notice clarity.<br>
C_chain = downstream chain visibility.<br>
E_enforcement = enforcement ability and confirmation returned.<br>
P_residue = residual copy/cache risk plus downstream drift pressure.
</p>
</div></section>
<section><div class="wrap card">
<h2>Route transparency / math disclosure</h2>
<p class="readbox">
<b>Why this matters:</b><br>
Consent granted is not enough. Consent carried is the real test.<br>
Revocation received is not enough. Revocation propagated is the real test.<br>
Revocation propagates as a boundary delta, not erasure.
</p>
<details class="logicBox" open>
<summary>Show route logic / show the math</summary>
<div class="logicGrid">
<div class="logicMini">
<b>Consent carry path</b><br>
A_authorization = 0.25×Explicit Authorization + 0.20×Informed Understanding + 0.18×Current Consent + 0.20×Identity/Authority Match + 0.17×Consent Record<br><br>
B_scope = 0.24×Purpose Clarity + 0.20×Recipient Clarity + 0.22×Data/Action Boundary + 0.16×Duration Clarity + 0.18×Revocation Path Visible<br><br>
S_consent = A_authorization × B_scope − P_revocation_drift
</div>
<div class="logicMini">
<b>Revocation propagation path</b><br>
P_revocation_drift = average downstream branch count, transfer opacity, retention friction, automation lag, and institutional/commercial incentive.<br><br>
S_revocation = R_notice × C_chain × E_enforcement − P_residue<br><br>
P_residue = residual copy/cache risk plus retention, opacity, and automation residue.
</div>
<div class="logicMini">
<b>Override order</b><br>
Repair debt overrides ordinary consent evaluation. Full revocation overrides active consent. Downstream quarantine overrides convenience when branches cannot be verified. Sensitive/high-stakes contexts can require human review.
</div>
<div class="logicMini">
<b>Boundary rule</b><br>
Revocation does not erase the audit spine. It travels forward as a boundary delta: stop, downgrade, quarantine, re-proof, retain with limits, or repair according to affected scope.
</div>
</div>
<table class="routeProofTable">
<thead>
<tr><th>Preset</th><th>Consent Carry Route</th><th>Revocation Route</th><th>Consent Stability</th><th>Revocation Stability</th></tr>
</thead>
<tbody>
<tr><td>Clean Bounded Consent</td><td>CONSENT_ACTIVE</td><td>REVOCATION_READY</td><td>0.58</td><td>0.29</td></tr>
<tr><td>Ambiguous App Permission</td><td>SCOPE_MISMATCH</td><td>NO_REVOCATION_ACTIVE</td><td>-0.44</td><td>-0.59</td></tr>
<tr><td>Patient EHR Consent Revocation</td><td>REVOKED_SCOPE</td><td>DOWNSTREAM_QUARANTINE</td><td>-0.14</td><td>-0.47</td></tr>
<tr><td>Health Data Partial Revocation</td><td>PARTIAL_REVOCATION</td><td>HUMAN_REVIEW_REQUIRED</td><td>-0.01</td><td>-0.30</td></tr>
<tr><td>Caregiver / Proxy Boundary Drift</td><td>HUMAN_REVIEW_REQUIRED</td><td>NO_REVOCATION_ACTIVE</td><td>-0.17</td><td>-0.35</td></tr>
<tr><td>AI Agent Tool Permission Drift</td><td>SCOPE_CLARIFICATION_REQUIRED</td><td>NO_REVOCATION_ACTIVE</td><td>-0.34</td><td>-0.47</td></tr>
<tr><td>Public Platform Data Reuse</td><td>RETENTION_LIMIT_REQUIRED</td><td>NO_REVOCATION_ACTIVE</td><td>-0.63</td><td>-0.76</td></tr>
<tr><td>Revocation Lost Downstream</td><td>REVOKED_SCOPE</td><td>DOWNSTREAM_QUARANTINE</td><td>-0.38</td><td>-0.78</td></tr>
<tr><td>Prior Consent Boundary Harm</td><td>REPAIR_REQUIRED</td><td>REPAIR_REQUIRED</td><td>-0.52</td><td>-0.71</td></tr>
</tbody>
</table>
</details>
</div></section>
<section><div class="wrap card">
<h2>Route stability / sensitivity</h2>
<p class="readbox">
A boundary route can be stable, fragile, threshold-adjacent, or override-locked.
This patch perturbs each slider by ±10% and checks whether the recommended consent or revocation routes change.
v0.0.4 also records the flip trace: which slider moved, in which direction, and which route it changed from/to.
</p>
<div class="logicGrid">
<div class="logicMini"><b>Stable Route</b><br>The same route survives small pressure changes. The boundary diagnosis is not near a threshold.</div>
<div class="logicMini"><b>Threshold Adjacent</b><br>A few small slider shifts change the route. The case is close enough to a boundary that review or re-proof may be prudent.</div>
<div class="logicMini"><b>Fragile Route</b><br>Many small slider shifts change the route. The boundary state is unstable and should not be treated as cleanly resolved.</div>
<div class="logicMini"><b>Override Locked</b><br>A hard gate such as repair debt, full revocation, or partial revocation is active. Slider softness does not override the boundary gate.</div>
</div>
</div></section>
<section><div class="wrap card">
<h2>Route taxonomy</h2>
<div class="taxonomy" id="taxonomy"></div>
</div></section>
<section><div class="wrap card">
<h2>Defensive boundary</h2>
<p class="readbox">
This simulator maps consent-boundary degradation for review, repair, education, and defensive design. It does not provide instructions for bypassing consent, evading revocation, retaining data improperly, or exploiting downstream copies.
</p>
</div></section>
</main>
<footer>
<div class="wrap">
<strong>Consent / Revocation Propagation Simulator v0.0.4</strong><br/>
Sensitivity Drivers + Flip Trace Patch. Boundary: not legal advice, not HIPAA compliance certification, not privacy-law analysis, not clinical authorization.
</div>
</footer>
<script>
const sliders=[
"explicitAuthorization","informedUnderstanding","currentConsent","identityAuthority","consentRecord",
"purposeClarity","recipientClarity","dataActionBoundary","durationClarity","revocationPathVisible",
"downstreamBranchCount","transferOpacity","retentionFriction","automationLag","institutionalIncentive",
"revocationNotice","chainVisibility","enforcementAbility","confirmationReturned","residualCopyRisk"
];
const toggles=["consentGranted","partialRevocation","fullRevocation","downstreamSharing","priorConsentHarm","sensitiveContext"];
function el(id){return document.getElementById(id)} function val(id){return parseFloat(el(id).value)} function avg(a){return a.reduce((x,y)=>x+y,0)/a.length} function r(n){return Number(n).toFixed(2)}
const consentRoutes={
CONSENT_ACTIVE:["CONSENT ACTIVE","Consent is clear, current, scoped, traceable, and enforceable for the evaluated use.","The boundary is intact for this scope. Consent can carry with audit spine attached.","Allow use inside the stated scope. Keep revocation path visible."],
BOUNDED_CONSENT:["BOUNDED CONSENT","Consent exists, but only inside a narrow or explicitly bounded scope.","The boundary can carry, but it cannot expand beyond its stated purpose, recipient, duration, or action limits.","Allow only the bounded use. Do not reuse or extend without re-proof."],
SCOPE_CLARIFICATION_REQUIRED:["SCOPE CLARIFICATION REQUIRED","The system cannot evaluate consent because purpose, recipient, duration, or action boundary is unclear.","Honesty requires naming the actual scope before consent can be treated as valid.","Clarify scope before use, transfer, automation, or reuse."],
SCOPE_MISMATCH:["SCOPE MISMATCH","The proposed use exceeds or differs from the consented scope.","OAM detected: a boundary is being stretched beyond what was authorized.","Reject or narrow the use. Return to the consented scope or request renewed authorization."],
REPROOF_REQUIRED:["REPROOF REQUIRED","Consent may exist, but it is stale, ambiguous, transferred, sensitive, or high-stakes enough to require renewed authorization.","Integrity blocks silent inheritance across scope, time, stakeholder, or context drift.","Pause use and request re-proof before proceeding."],
PARTIAL_REVOCATION:["PARTIAL REVOCATION","The person narrowed consent rather than fully revoking it.","Respect preserves the remaining allowed scope while downgrading or blocking the revoked branch.","Update the boundary. Allow only what remains explicitly authorized."],
REVOKED_SCOPE:["REVOKED SCOPE","Consent has been withdrawn for the affected scope. Continued use cannot inherit authorization.","Revocation propagates as a boundary delta, not erasure. Prior history remains, but forward authorization is downgraded.","Stop affected use. Preserve audit trail. Propagate boundary delta downstream."],
DOWNSTREAM_QUARANTINE:["DOWNSTREAM QUARANTINE","Downstream branches are affected but cannot be verified.","The boundary update cannot be trusted to have reached every branch.","Quarantine affected branches until chain visibility, enforcement, and confirmation improve."],
RETENTION_LIMIT_REQUIRED:["RETENTION LIMIT REQUIRED","Use may be bounded, but retention, cache, archive, or deletion handling is not clear enough.","Respect requires visible retention limits when data/action residue can persist after the original use.","Set retention limits, downgrade copies, mark archives, or request human review."],
REPAIR_REQUIRED:["REPAIR REQUIRED","A prior consent boundary was violated, erased, ignored, or misrepresented.","Repair debt overrides ordinary consent evaluation.","Reopen for repair. Name the boundary harm, preserve affected party agency, correct downstream records, and define prevention steps."],
HUMAN_REVIEW_REQUIRED:["HUMAN REVIEW REQUIRED","The context is sensitive or high-stakes and automated routing is not enough.","Respect requires accountable human review before the boundary is inherited, reused, or downgraded.","Pause automation. Route to qualified human review with scope and revocation history visible."]
};
const revocationRoutes={
NO_REVOCATION_ACTIVE:["NO REVOCATION ACTIVE","No current revocation event is being evaluated.","Consent status is evaluated through the carry path unless a revocation, partial revocation, or boundary harm is present.","Keep revocation path visible and ready."],
REVOCATION_READY:["REVOCATION READY","The system has enough notice, chain visibility, enforcement, and confirmation to propagate a future revocation.","The boundary has a usable return path if consent changes.","Proceed within scope. Keep receipt and enforcement path auditable."],
PARTIAL_REVOCATION_PROPAGATING:["PARTIAL REVOCATION PROPAGATING","A narrowed boundary is moving downstream.","The original authorization is not erased; the allowed scope is rewritten and must reach affected branches.","Propagate the narrowed scope and confirm branch compliance."],
REVOCATION_PROPAGATING:["REVOCATION PROPAGATING","Revocation has been expressed and is moving downstream, but not all branches have confirmed compliance.","The boundary delta is in motion, but enforcement is incomplete.","Continue propagation. Hold affected uses until confirmation returns."],
REVOKED_SCOPE_CONFIRMED:["REVOKED SCOPE CONFIRMED","The affected scope has been revoked and downstream confirmation is strong enough.","The boundary delta reached the known chain and forward use is downgraded or stopped.","Keep the audit spine and enforce the revoked scope going forward."],
DOWNSTREAM_QUARANTINE:["DOWNSTREAM QUARANTINE","Downstream branches are affected but cannot be verified.","The revocation may not have reached all copies, caches, partners, or automated branches.","Quarantine affected downstream use until visibility and enforcement are proven."],
RETENTION_LIMIT_REQUIRED:["RETENTION LIMIT REQUIRED","Retention residue remains too high for clean revocation propagation.","The boundary delta reached the system, but copies/caches/archives still require scoped handling.","Define deletion, downgrade, archive, or legal/operational retention handling."],
REPAIR_REQUIRED:["REPAIR REQUIRED","A prior boundary harm or revocation failure requires repair before ordinary propagation.","Revocation failure has become a repair loop, not merely a status update.","Name the harm, notify affected parties where appropriate, correct records, and prevent recurrence."],
HUMAN_REVIEW_REQUIRED:["HUMAN REVIEW REQUIRED","The revocation context is sensitive, ambiguous, or high-stakes enough to require human review.","The boundary should not be resolved by automation alone.","Route for human review with chain, scope, retention, and affected branches visible."]
};
const routeTriggers={
CONSENT_ACTIVE:"Trigger: no revocation/harm gates, high authorization support, high scope support, low drift pressure, and consent currently granted.",
BOUNDED_CONSENT:"Trigger: consent support is adequate but scope is not strong enough for broad inheritance.",
SCOPE_CLARIFICATION_REQUIRED:"Trigger: purpose, recipient, data/action boundary, duration, or revocation path is below clarity threshold.",
SCOPE_MISMATCH:"Trigger: consent exists, but scope support is weak while drift/reuse pressure is high.",
REPROOF_REQUIRED:"Trigger: sensitive/high-stakes context, stale consent, transferred use, weak record, or high uncertainty requires renewed authorization.",
PARTIAL_REVOCATION:"Trigger: partial revocation gate is active.",
REVOKED_SCOPE:"Trigger: full revocation gate is active.",
DOWNSTREAM_QUARANTINE:"Trigger: downstream sharing occurred and chain visibility/enforcement/confirmation are too weak.",
RETENTION_LIMIT_REQUIRED:"Trigger: retention friction or residual copy/cache risk is high.",
REPAIR_REQUIRED:"Trigger: prior consent boundary harm is present.",
HUMAN_REVIEW_REQUIRED:"Trigger: sensitive or high-stakes context with weak consent, scope, or propagation support.",
NO_REVOCATION_ACTIVE:"Trigger: no partial or full revocation is active.",
REVOCATION_READY:"Trigger: revocation path, chain visibility, enforcement, and confirmation are strong enough for future propagation.",
PARTIAL_REVOCATION_PROPAGATING:"Trigger: partial revocation is active and downstream handling is in progress.",
REVOCATION_PROPAGATING:"Trigger: full revocation is active, but not all downstream confirmations have returned.",
REVOKED_SCOPE_CONFIRMED:"Trigger: full revocation is active with strong chain visibility, enforcement, confirmation, and manageable residue pressure."
};
const flagDefs={
RepairDebt:"A prior consent boundary was violated, ignored, erased, or misrepresented.",
FullRevocation:"Consent has been withdrawn for the affected scope.",
PartialRevocation:"Consent has been narrowed; only remaining explicitly authorized scope can continue.",
DownstreamOpacity:"Downstream branches, partners, copies, or automations are not visible enough.",
ScopeAmbiguity:"Purpose, recipient, action/data boundary, duration, or revocation path is unclear.",
PurposeMismatch:"Purpose clarity is too weak for the proposed use.",
RecipientUnclear:"Recipient clarity is weak.",
RevocationPathMissing:"The route for withdrawing or narrowing consent is not visible enough.",
ConsentStale:"Consent is not current enough to carry safely.",
RecordWeak:"Consent record or provenance is weak.",
RetentionResidue:"Retention friction or residual copy/cache risk is high.",
AutomationLag:"Automation lag could let old authorization continue after boundary change.",
SensitiveContext:"High-stakes context requires extra caution or human review.",
ReproofNeeded:"Renewed authorization is needed before scope inheritance.",
QuarantineNeeded:"Affected downstream branches should be quarantined until status is verified.",
HumanReview:"Automation is insufficient for this boundary decision.",
ConsentClear:"Consent boundary appears clear for the evaluated scope.",
RevocationReady:"Revocation path appears ready if consent changes."
};
const presets={
clean_bounded:["Clean Bounded Consent",{explicitAuthorization:.92,informedUnderstanding:.88,currentConsent:.90,identityAuthority:.90,consentRecord:.86,purposeClarity:.88,recipientClarity:.84,dataActionBoundary:.86,durationClarity:.82,revocationPathVisible:.86,downstreamBranchCount:.20,transferOpacity:.18,retentionFriction:.22,automationLag:.14,institutionalIncentive:.20,revocationNotice:.80,chainVisibility:.78,enforcementAbility:.80,confirmationReturned:.76,residualCopyRisk:.20,consentGranted:true,partialRevocation:false,fullRevocation:false,downstreamSharing:false,priorConsentHarm:false,sensitiveContext:false}],
ambiguous_app:["Ambiguous App Permission",{explicitAuthorization:.62,informedUnderstanding:.42,currentConsent:.74,identityAuthority:.76,consentRecord:.54,purposeClarity:.34,recipientClarity:.32,dataActionBoundary:.40,durationClarity:.36,revocationPathVisible:.30,downstreamBranchCount:.66,transferOpacity:.72,retentionFriction:.62,automationLag:.52,institutionalIncentive:.74,revocationNotice:.42,chainVisibility:.30,enforcementAbility:.34,confirmationReturned:.28,residualCopyRisk:.64,consentGranted:true,partialRevocation:false,fullRevocation:false,downstreamSharing:true,priorConsentHarm:false,sensitiveContext:false}],
patient_ehr_revocation:["Patient EHR Consent Revocation",{explicitAuthorization:.74,informedUnderstanding:.70,currentConsent:.54,identityAuthority:.84,consentRecord:.82,purposeClarity:.68,recipientClarity:.62,dataActionBoundary:.58,durationClarity:.56,revocationPathVisible:.72,downstreamBranchCount:.72,transferOpacity:.58,retentionFriction:.78,automationLag:.50,institutionalIncentive:.42,revocationNotice:.86,chainVisibility:.52,enforcementAbility:.60,confirmationReturned:.42,residualCopyRisk:.76,consentGranted:false,partialRevocation:false,fullRevocation:true,downstreamSharing:true,priorConsentHarm:false,sensitiveContext:true}],
health_partial_revocation:["Health Data Partial Revocation",{explicitAuthorization:.80,informedUnderstanding:.76,currentConsent:.62,identityAuthority:.86,consentRecord:.84,purposeClarity:.74,recipientClarity:.68,dataActionBoundary:.66,durationClarity:.60,revocationPathVisible:.78,downstreamBranchCount:.68,transferOpacity:.50,retentionFriction:.64,automationLag:.46,institutionalIncentive:.48,revocationNotice:.82,chainVisibility:.58,enforcementAbility:.64,confirmationReturned:.50,residualCopyRisk:.60,consentGranted:true,partialRevocation:true,fullRevocation:false,downstreamSharing:true,priorConsentHarm:false,sensitiveContext:true}],
caregiver_proxy_drift:["Caregiver / Proxy Boundary Drift",{explicitAuthorization:.54,informedUnderstanding:.50,currentConsent:.66,identityAuthority:.38,consentRecord:.48,purposeClarity:.56,recipientClarity:.50,dataActionBoundary:.44,durationClarity:.44,revocationPathVisible:.42,downstreamBranchCount:.42,transferOpacity:.50,retentionFriction:.44,automationLag:.34,institutionalIncentive:.38,revocationNotice:.42,chainVisibility:.46,enforcementAbility:.48,confirmationReturned:.40,residualCopyRisk:.44,consentGranted:true,partialRevocation:false,fullRevocation:false,downstreamSharing:false,priorConsentHarm:false,sensitiveContext:true}],
ai_agent_permission_drift:["AI Agent Tool Permission Drift",{explicitAuthorization:.68,informedUnderstanding:.58,currentConsent:.46,identityAuthority:.70,consentRecord:.64,purposeClarity:.44,recipientClarity:.58,dataActionBoundary:.36,durationClarity:.32,revocationPathVisible:.46,downstreamBranchCount:.54,transferOpacity:.62,retentionFriction:.46,automationLag:.82,institutionalIncentive:.58,revocationNotice:.52,chainVisibility:.48,enforcementAbility:.42,confirmationReturned:.30,residualCopyRisk:.50,consentGranted:true,partialRevocation:false,fullRevocation:false,downstreamSharing:true,priorConsentHarm:false,sensitiveContext:false}],
platform_reuse:["Public Platform Data Reuse",{explicitAuthorization:.58,informedUnderstanding:.38,currentConsent:.64,identityAuthority:.70,consentRecord:.46,purposeClarity:.30,recipientClarity:.28,dataActionBoundary:.32,durationClarity:.26,revocationPathVisible:.24,downstreamBranchCount:.86,transferOpacity:.82,retentionFriction:.76,automationLag:.62,institutionalIncentive:.88,revocationNotice:.36,chainVisibility:.22,enforcementAbility:.28,confirmationReturned:.16,residualCopyRisk:.82,consentGranted:true,partialRevocation:false,fullRevocation:false,downstreamSharing:true,priorConsentHarm:false,sensitiveContext:false}],
revocation_lost_downstream:["Revocation Lost Downstream",{explicitAuthorization:.70,informedUnderstanding:.66,currentConsent:.40,identityAuthority:.80,consentRecord:.76,purposeClarity:.64,recipientClarity:.56,dataActionBoundary:.58,durationClarity:.50,revocationPathVisible:.70,downstreamBranchCount:.88,transferOpacity:.84,retentionFriction:.82,automationLag:.74,institutionalIncentive:.64,revocationNotice:.88,chainVisibility:.24,enforcementAbility:.30,confirmationReturned:.14,residualCopyRisk:.86,consentGranted:false,partialRevocation:false,fullRevocation:true,downstreamSharing:true,priorConsentHarm:false,sensitiveContext:false}],
prior_boundary_harm:["Prior Consent Boundary Harm",{explicitAuthorization:.46,informedUnderstanding:.40,currentConsent:.36,identityAuthority:.62,consentRecord:.32,purposeClarity:.34,recipientClarity:.36,dataActionBoundary:.30,durationClarity:.28,revocationPathVisible:.22,downstreamBranchCount:.62,transferOpacity:.70,retentionFriction:.74,automationLag:.62,institutionalIncentive:.58,revocationNotice:.42,chainVisibility:.30,enforcementAbility:.28,confirmationReturned:.18,residualCopyRisk:.78,consentGranted:false,partialRevocation:false,fullRevocation:false,downstreamSharing:true,priorConsentHarm:true,sensitiveContext:true}]
};
function compute(){
const explicitAuthorization=val("explicitAuthorization"), informedUnderstanding=val("informedUnderstanding"), currentConsent=val("currentConsent"), identityAuthority=val("identityAuthority"), consentRecord=val("consentRecord");
const purposeClarity=val("purposeClarity"), recipientClarity=val("recipientClarity"), dataActionBoundary=val("dataActionBoundary"), durationClarity=val("durationClarity"), revocationPathVisible=val("revocationPathVisible");
const downstreamBranchCount=val("downstreamBranchCount"), transferOpacity=val("transferOpacity"), retentionFriction=val("retentionFriction"), automationLag=val("automationLag"), institutionalIncentive=val("institutionalIncentive");
const revocationNotice=val("revocationNotice"), chainVisibility=val("chainVisibility"), enforcementAbility=val("enforcementAbility"), confirmationReturned=val("confirmationReturned"), residualCopyRisk=val("residualCopyRisk");
const consentGranted=el("consentGranted").checked, partialRevocation=el("partialRevocation").checked, fullRevocation=el("fullRevocation").checked, downstreamSharing=el("downstreamSharing").checked, priorConsentHarm=el("priorConsentHarm").checked, sensitiveContext=el("sensitiveContext").checked;
const aAuthorization=.25*explicitAuthorization+.20*informedUnderstanding+.18*currentConsent+.20*identityAuthority+.17*consentRecord;
const bScope=.24*purposeClarity+.20*recipientClarity+.22*dataActionBoundary+.16*durationClarity+.18*revocationPathVisible;
const revocationDrift=avg([downstreamBranchCount,transferOpacity,retentionFriction,automationLag,institutionalIncentive]);
const authorizationSupport=aAuthorization;
const scopeSupport=bScope;
const consentSupport=aAuthorization*bScope;
const consentStability=consentSupport-revocationDrift;
const residuePressure=.50*residualCopyRisk+.20*retentionFriction+.15*transferOpacity+.15*automationLag;
const revocationSupport=revocationNotice*chainVisibility*((enforcementAbility+confirmationReturned)/2);
const revocationStability=revocationSupport-residuePressure;
const boundaryDeltaStrength=fullRevocation||partialRevocation ? revocationNotice*((chainVisibility+enforcementAbility+confirmationReturned)/3) : revocationPathVisible*((chainVisibility+enforcementAbility)/2);
let consentFlags=[];
if(priorConsentHarm) consentFlags.push(["RepairDebt","hot"]);
if(fullRevocation) consentFlags.push(["FullRevocation","hot"]);
if(partialRevocation) consentFlags.push(["PartialRevocation","warn"]);
if(downstreamSharing && (chainVisibility<.50 || transferOpacity>.65)) consentFlags.push(["DownstreamOpacity","warn"]);
if(bScope<.50) consentFlags.push(["ScopeAmbiguity","warn"]);
if(purposeClarity<.45) consentFlags.push(["PurposeMismatch","warn"]);
if(recipientClarity<.45) consentFlags.push(["RecipientUnclear","warn"]);
if(revocationPathVisible<.45) consentFlags.push(["RevocationPathMissing","warn"]);
if(currentConsent<.50) consentFlags.push(["ConsentStale","warn"]);
if(consentRecord<.50) consentFlags.push(["RecordWeak","warn"]);
if(retentionFriction>.68 || residualCopyRisk>.68) consentFlags.push(["RetentionResidue","warn"]);
if(automationLag>.70) consentFlags.push(["AutomationLag","warn"]);
if(sensitiveContext) consentFlags.push(["SensitiveContext","info"]);
let revocationFlags=[];
if(priorConsentHarm) revocationFlags.push(["RepairDebt","hot"]);
if(fullRevocation) revocationFlags.push(["FullRevocation","hot"]);
if(partialRevocation) revocationFlags.push(["PartialRevocation","warn"]);
if(downstreamSharing && (chainVisibility<.50 || transferOpacity>.65)) revocationFlags.push(["DownstreamOpacity","warn"]);
if(retentionFriction>.68 || residualCopyRisk>.68) revocationFlags.push(["RetentionResidue","warn"]);
if(automationLag>.70) revocationFlags.push(["AutomationLag","warn"]);
if(sensitiveContext) revocationFlags.push(["SensitiveContext","info"]);
let consentRoute="BOUNDED_CONSENT";
if(priorConsentHarm) consentRoute="REPAIR_REQUIRED";
else if(fullRevocation) consentRoute="REVOKED_SCOPE";
else if(downstreamSharing && (chainVisibility<.35 || enforcementAbility<.35 || confirmationReturned<.25) && (fullRevocation || partialRevocation || revocationNotice>.70)) consentRoute="DOWNSTREAM_QUARANTINE";
else if(partialRevocation) consentRoute="PARTIAL_REVOCATION";
else if((retentionFriction>.76 || residualCopyRisk>.76) && downstreamSharing) consentRoute="RETENTION_LIMIT_REQUIRED";
else if(sensitiveContext && (consentSupport<.52 || bScope<.58 || aAuthorization<.58)) consentRoute="HUMAN_REVIEW_REQUIRED";
else if(consentGranted && bScope<.42 && revocationDrift>.55) consentRoute="SCOPE_MISMATCH";
else if(consentGranted && (bScope<.50 || purposeClarity<.45 || recipientClarity<.45 || dataActionBoundary<.45 || durationClarity<.38 || revocationPathVisible<.40)) consentRoute="SCOPE_CLARIFICATION_REQUIRED";
else if(!consentGranted || currentConsent<.50 || consentRecord<.45 || (sensitiveContext && consentSupport<.66)) consentRoute="REPROOF_REQUIRED";
else if(consentSupport>=.70 && revocationDrift<.38 && consentGranted) consentRoute="CONSENT_ACTIVE";
else consentRoute="BOUNDED_CONSENT";
let revocationRoute="NO_REVOCATION_ACTIVE";
if(priorConsentHarm) revocationRoute="REPAIR_REQUIRED";
else if(fullRevocation && revocationSupport>=.55 && residuePressure<.45 && confirmationReturned>=.65) revocationRoute="REVOKED_SCOPE_CONFIRMED";
else if(fullRevocation && downstreamSharing && (chainVisibility<.45 || enforcementAbility<.45 || confirmationReturned<.45)) revocationRoute="DOWNSTREAM_QUARANTINE";
else if((fullRevocation || partialRevocation) && (retentionFriction>.76 || residualCopyRisk>.76)) revocationRoute="RETENTION_LIMIT_REQUIRED";
else if(sensitiveContext && (fullRevocation || partialRevocation) && (revocationSupport<.50 || residuePressure>.55)) revocationRoute="HUMAN_REVIEW_REQUIRED";
else if(partialRevocation) revocationRoute="PARTIAL_REVOCATION_PROPAGATING";
else if(fullRevocation) revocationRoute="REVOCATION_PROPAGATING";
else if(revocationPathVisible>=.72 && chainVisibility>=.65 && enforcementAbility>=.65 && confirmationReturned>=.55 && residuePressure<.50) revocationRoute="REVOCATION_READY";
else revocationRoute="NO_REVOCATION_ACTIVE";
if(!consentFlags.length) consentFlags.push(["ConsentClear","good"]);
if(!revocationFlags.length) revocationFlags.push(["RevocationReady","good"]);
if(consentRoute==="REPROOF_REQUIRED") consentFlags.push(["ReproofNeeded","warn"]);
if(consentRoute==="DOWNSTREAM_QUARANTINE" || revocationRoute==="DOWNSTREAM_QUARANTINE") { consentFlags.push(["QuarantineNeeded","hot"]); revocationFlags.push(["QuarantineNeeded","hot"]); }
if(consentRoute==="HUMAN_REVIEW_REQUIRED" || revocationRoute==="HUMAN_REVIEW_REQUIRED") { consentFlags.push(["HumanReview","warn"]); revocationFlags.push(["HumanReview","warn"]); }
return {explicitAuthorization,informedUnderstanding,currentConsent,identityAuthority,consentRecord,purposeClarity,recipientClarity,dataActionBoundary,durationClarity,revocationPathVisible,downstreamBranchCount,transferOpacity,retentionFriction,automationLag,institutionalIncentive,revocationNotice,chainVisibility,enforcementAbility,confirmationReturned,residualCopyRisk,consentGranted,partialRevocation,fullRevocation,downstreamSharing,priorConsentHarm,sensitiveContext,aAuthorization,bScope,authorizationSupport,scopeSupport,revocationDrift,consentSupport,consentStability,residuePressure,revocationSupport,revocationStability,boundaryDeltaStrength,consentRoute,revocationRoute,consentFlags,revocationFlags};
}
function routeWhy(c){
let consentWhy="";
if(c.consentRoute==="REPAIR_REQUIRED") consentWhy="REPAIR_REQUIRED triggered because prior consent boundary harm is present. Repair debt overrides ordinary consent evaluation.";
else if(c.consentRoute==="REVOKED_SCOPE") consentWhy="REVOKED_SCOPE triggered because full revocation is active for the affected scope. Forward use cannot inherit authorization.";
else if(c.consentRoute==="DOWNSTREAM_QUARANTINE") consentWhy="DOWNSTREAM_QUARANTINE triggered because revocation or boundary change affects downstream branches that cannot be verified.";
else if(c.consentRoute==="PARTIAL_REVOCATION") consentWhy="PARTIAL_REVOCATION triggered because consent has been narrowed. Remaining use must fit the new boundary.";
else if(c.consentRoute==="RETENTION_LIMIT_REQUIRED") consentWhy="RETENTION_LIMIT_REQUIRED triggered because retention friction or residual copy/cache risk is high enough to require scoped handling.";
else if(c.consentRoute==="HUMAN_REVIEW_REQUIRED") consentWhy="HUMAN_REVIEW_REQUIRED triggered because sensitive/high-stakes context combines with weak consent or scope support.";
else if(c.consentRoute==="SCOPE_MISMATCH") consentWhy="SCOPE_MISMATCH triggered because proposed use pressure exceeds the clarity of the consented scope.";
else if(c.consentRoute==="SCOPE_CLARIFICATION_REQUIRED") consentWhy="SCOPE_CLARIFICATION_REQUIRED triggered because purpose, recipient, duration, action boundary, or revocation path is unclear.";
else if(c.consentRoute==="REPROOF_REQUIRED") consentWhy="REPROOF_REQUIRED triggered because consent is stale, absent, weakly recorded, transferred, or high-stakes enough to require renewed authorization.";
else if(c.consentRoute==="CONSENT_ACTIVE") consentWhy="CONSENT_ACTIVE triggered because authorization support and scope support are high while drift pressure is low.";
else consentWhy="BOUNDED_CONSENT triggered because consent exists, but only inside a limited scope that should not silently expand.";
let revocationWhy="";
if(c.revocationRoute==="REPAIR_REQUIRED") revocationWhy="REPAIR_REQUIRED triggered because a prior revocation or consent-boundary failure requires repair before ordinary propagation.";
else if(c.revocationRoute==="REVOKED_SCOPE_CONFIRMED") revocationWhy="REVOKED_SCOPE_CONFIRMED triggered because revocation is active and downstream confirmation is strong enough.";
else if(c.revocationRoute==="DOWNSTREAM_QUARANTINE") revocationWhy="DOWNSTREAM_QUARANTINE triggered because affected downstream branches cannot be verified.";
else if(c.revocationRoute==="RETENTION_LIMIT_REQUIRED") revocationWhy="RETENTION_LIMIT_REQUIRED triggered because retained copies, caches, archives, or operational/legal residue require scoped handling.";
else if(c.revocationRoute==="HUMAN_REVIEW_REQUIRED") revocationWhy="HUMAN_REVIEW_REQUIRED triggered because sensitive or high-stakes revocation cannot be completed by automation alone.";
else if(c.revocationRoute==="PARTIAL_REVOCATION_PROPAGATING") revocationWhy="PARTIAL_REVOCATION_PROPAGATING triggered because narrowed consent is moving downstream.";
else if(c.revocationRoute==="REVOCATION_PROPAGATING") revocationWhy="REVOCATION_PROPAGATING triggered because full revocation is active but not yet fully confirmed across the downstream chain.";
else if(c.revocationRoute==="REVOCATION_READY") revocationWhy="REVOCATION_READY triggered because notice, chain visibility, enforcement, and confirmation support are strong enough for future revocation.";
else revocationWhy="NO_REVOCATION_ACTIVE triggered because no partial or full revocation is currently active.";
return {consentWhy,revocationWhy};
}
function renderFlags(id, flags){
el(id).innerHTML="";
flags.forEach(([name,cls])=>{let s=document.createElement("span");s.className="flag "+cls;s.textContent=name;el(id).appendChild(s)});
}
function update(){
sliders.forEach(id=>el(id+"Val").textContent=r(val(id)));
const c=compute(), consent=consentRoutes[c.consentRoute], rev=revocationRoutes[c.revocationRoute], why=routeWhy(c);
el("consentState").textContent=consent[0]; el("consentMeaning").textContent=consent[1]; el("consentRead").textContent=consent[2]; el("consentWhy").textContent=why.consentWhy; el("consentAction").textContent=consent[3];
el("authorizationSupport").textContent=r(c.authorizationSupport); el("scopeSupport").textContent=r(c.scopeSupport); el("consentStability").textContent=r(c.consentStability); el("revocationDrift").textContent=r(c.revocationDrift); renderFlags("consentFlags",c.consentFlags);
el("revocationState").textContent=rev[0]; el("revocationMeaning").textContent=rev[1]; el("revocationRead").textContent=rev[2]; el("revocationWhy").textContent=why.revocationWhy; el("revocationAction").textContent=rev[3];
el("revocationSupport").textContent=r(c.revocationSupport); el("residuePressure").textContent=r(c.residuePressure); el("revocationStability").textContent=r(c.revocationStability); el("boundaryDeltaStrength").textContent=r(c.boundaryDeltaStrength); renderFlags("revocationFlags",c.revocationFlags);
if(urlSyncReady) writeUrlState();
}
function applyPreset(k){
const p=presets[k][1];
Object.entries(p).forEach(([key,v])=>{if(typeof v==="boolean")el(key).checked=v;else el(key).value=v});
update();
}
function report(){
const c=compute(), consent=consentRoutes[c.consentRoute], rev=revocationRoutes[c.revocationRoute], why=routeWhy(c), preset=presets[el("preset").value]?.[0]||"Manual exploration";
el("report").value=`Consent / Revocation Propagation Simulator v0.0.4 — Living Boundary Report
Scenario:
${preset}
Boundary:
This simulator does not provide legal advice, HIPAA compliance certification, privacy-law analysis, or clinical authorization. It maps whether a consent boundary remains structurally intact under handoff, downstream reuse, retention, and revocation pressure.
Original Consent / Boundary:
${el("originalConsent").value}
Downstream Use / Revocation Event:
${el("downstreamEvent").value}
Consent Carry State:
- Recommended State: ${consent[0]}
- A_authorization / Authorization Support: ${r(c.authorizationSupport)}
- B_scope / Scope Support: ${r(c.scopeSupport)}
- P_revocation_drift / Drift Pressure: ${r(c.revocationDrift)}
- Consent Stability: ${r(c.consentStability)}
- Flags:
${c.consentFlags.map(f=>"- "+f[0]+": "+(flagDefs[f[0]]||"No definition available.")).join("\n")}
- Read: ${consent[2]}
- Why this route: ${why.consentWhy}
- Action: ${consent[3]}
Revocation Propagation State:
- Recommended State: ${rev[0]}
- Revocation Support: ${r(c.revocationSupport)}
- Residue Pressure: ${r(c.residuePressure)}
- Propagation Stability: ${r(c.revocationStability)}
- Boundary Delta Strength: ${r(c.boundaryDeltaStrength)}
- Flags:
${c.revocationFlags.map(f=>"- "+f[0]+": "+(flagDefs[f[0]]||"No definition available.")).join("\n")}
- Read: ${rev[2]}
- Why this route: ${why.revocationWhy}
- Action: ${rev[3]}
Core equations:
S_consent = A_authorization × B_scope − P_revocation_drift
S_revocation = R_notice × C_chain × E_enforcement − P_residue
Route transparency note:
Repair, revocation, downstream quarantine, retention limits, and human review can override ordinary consent-active routing. The route is the product; scores support the route.
Sensitivity Analysis:
${lastSensitivity ? `- Overall: ${lastSensitivity.overall}\n- Consent route stability: ${lastSensitivity.consentStabilityLabel}\n- Revocation route stability: ${lastSensitivity.revocationStabilityLabel}\n- Consent route changes under ±10% perturbation: ${lastSensitivity.consentChanges}\n- Revocation route changes under ±10% perturbation: ${lastSensitivity.revocationChanges}\n- Top consent sensitivity drivers: ${driverSummary(lastSensitivity.consentFlips||[])}\n- Top revocation sensitivity drivers: ${driverSummary(lastSensitivity.revocationFlips||[])}\n- Consent flip trace: ${flipTraceSummary(lastSensitivity.consentFlips||[])}\n- Revocation flip trace: ${flipTraceSummary(lastSensitivity.revocationFlips||[])}` : "Not run for this report."}
Shareable State:
${encodeStateToURL()}
Public thesis:
Consent granted is not enough. Consent carried is the real test.
Revocation received is not enough. Revocation propagated is the real test.
Revocation propagates as a boundary delta, not erasure.`;
}
function renderTaxonomy(){
const box=el("taxonomy");
Object.entries(consentRoutes).forEach(([k,v])=>{let d=document.createElement("div");d.className="route-card";d.innerHTML=`<b>${v[0]}</b><p>${v[1]}</p><p><b>Trigger:</b> ${routeTriggers[k]||"Route condition depends on boundary state."}</p><p><b>Action:</b> ${v[3]}</p>`;box.appendChild(d)});
Object.entries(revocationRoutes).forEach(([k,v])=>{let d=document.createElement("div");d.className="route-card";d.innerHTML=`<b>${v[0]}</b><p>${v[1]}</p><p><b>Trigger:</b> ${routeTriggers[k]||"Route condition depends on propagation state."}</p><p><b>Action:</b> ${v[3]}</p>`;box.appendChild(d)});
}
const translations={
plain:"This tool checks whether a consent boundary stayed clear, scoped, current, revocable, and enforceable as it moved through downstream systems.",
patient:"Patient / EHR read: consent must preserve patient agency as records, AI summaries, caregiver/proxy reports, research reuse, and downstream recipients change. Revocation is a boundary update that must reach affected branches without erasing the audit spine.",
platform:"Platform / data reuse read: clicking yes once is not enough if the data later moves to partners, caches, training sets, ads, summaries, exports, or archives. Scope and revocation have to travel with the data.",
agent:"AI agent read: permission for one task does not automatically authorize every later tool call. If task scope changes, the agent should re-proof authorization instead of inheriting stale permission.",
system:"System map: Patient/User Intent → Consent Field → System Handoff → Downstream Receiver → Use / Retention / Deletion / Downgrade / Repair."
};
let urlSyncReady=false;
let lastSensitivity=null;
function getScenarioState(){
const state={preset:el("preset").value};
sliders.forEach(id=>{state[id]=parseFloat(el(id).value);});
toggles.forEach(id=>{state[id]=el(id).checked;});
state.originalConsent=el("originalConsent").value;
state.downstreamEvent=el("downstreamEvent").value;
return state;
}
function applyScenarioState(state){
if(state.preset && presets[state.preset]) el("preset").value=state.preset;
sliders.forEach(id=>{if(state[id]!==undefined && !Number.isNaN(parseFloat(state[id]))) el(id).value=Math.max(0,Math.min(1,parseFloat(state[id])));});
toggles.forEach(id=>{if(state[id]!==undefined) el(id).checked=(state[id]===true || state[id]==="1" || state[id]==="true");});
if(state.originalConsent!==undefined) el("originalConsent").value=state.originalConsent;
if(state.downstreamEvent!==undefined) el("downstreamEvent").value=state.downstreamEvent;
update();
}
function encodeStateToURL(){
const params=new URLSearchParams();
const state=getScenarioState();
Object.entries(state).forEach(([key,value])=>{
if(typeof value==="boolean") params.set(key,value?"1":"0");
else if(typeof value==="number") params.set(key,value.toFixed(3));
else if(typeof value==="string") params.set(key,value);
});
const url=new URL(window.location.href);
url.hash=params.toString();
return url.toString();
}
function loadStateFromURL(){
const hash=window.location.hash.substring(1);
if(!hash) return false;
const params=new URLSearchParams(hash);
const state={};
for(const [key,value] of params.entries()){
if(toggles.includes(key)) state[key]=value==="1";
else if(sliders.includes(key)) state[key]=parseFloat(value);
else state[key]=value;
}
if(Object.keys(state).length){applyScenarioState(state);return true;}
return false;
}
function writeUrlState(){
if(!urlSyncReady) return;
history.replaceState(null,"",encodeStateToURL());
}
function getCurrentStateObject(){
const state={};
sliders.forEach(id=>{state[id]=val(id);});
toggles.forEach(id=>{state[id]=el(id).checked;});
return state;
}
function clamp01(x){return Math.max(0,Math.min(1,x));}
function computeFromState(p){
const explicitAuthorization=p.explicitAuthorization, informedUnderstanding=p.informedUnderstanding, currentConsent=p.currentConsent, identityAuthority=p.identityAuthority, consentRecord=p.consentRecord;
const purposeClarity=p.purposeClarity, recipientClarity=p.recipientClarity, dataActionBoundary=p.dataActionBoundary, durationClarity=p.durationClarity, revocationPathVisible=p.revocationPathVisible;
const downstreamBranchCount=p.downstreamBranchCount, transferOpacity=p.transferOpacity, retentionFriction=p.retentionFriction, automationLag=p.automationLag, institutionalIncentive=p.institutionalIncentive;
const revocationNotice=p.revocationNotice, chainVisibility=p.chainVisibility, enforcementAbility=p.enforcementAbility, confirmationReturned=p.confirmationReturned, residualCopyRisk=p.residualCopyRisk;
const consentGranted=p.consentGranted, partialRevocation=p.partialRevocation, fullRevocation=p.fullRevocation, downstreamSharing=p.downstreamSharing, priorConsentHarm=p.priorConsentHarm, sensitiveContext=p.sensitiveContext;
const aAuthorization=.25*explicitAuthorization+.20*informedUnderstanding+.18*currentConsent+.20*identityAuthority+.17*consentRecord;
const bScope=.24*purposeClarity+.20*recipientClarity+.22*dataActionBoundary+.16*durationClarity+.18*revocationPathVisible;
const revocationDrift=avg([downstreamBranchCount,transferOpacity,retentionFriction,automationLag,institutionalIncentive]);
const consentSupport=aAuthorization*bScope;
const consentStability=consentSupport-revocationDrift;
const residuePressure=.50*residualCopyRisk+.20*retentionFriction+.15*transferOpacity+.15*automationLag;
const revocationSupport=revocationNotice*chainVisibility*((enforcementAbility+confirmationReturned)/2);
const revocationStability=revocationSupport-residuePressure;
let consentRoute="BOUNDED_CONSENT";
if(priorConsentHarm) consentRoute="REPAIR_REQUIRED";
else if(fullRevocation) consentRoute="REVOKED_SCOPE";
else if(downstreamSharing && (chainVisibility<.35 || enforcementAbility<.35 || confirmationReturned<.25) && (fullRevocation || partialRevocation || revocationNotice>.70)) consentRoute="DOWNSTREAM_QUARANTINE";
else if(partialRevocation) consentRoute="PARTIAL_REVOCATION";
else if((retentionFriction>.76 || residualCopyRisk>.76) && downstreamSharing) consentRoute="RETENTION_LIMIT_REQUIRED";
else if(sensitiveContext && (consentSupport<.52 || bScope<.58 || aAuthorization<.58)) consentRoute="HUMAN_REVIEW_REQUIRED";
else if(consentGranted && bScope<.42 && revocationDrift>.55) consentRoute="SCOPE_MISMATCH";
else if(consentGranted && (bScope<.50 || purposeClarity<.45 || recipientClarity<.45 || dataActionBoundary<.45 || durationClarity<.38 || revocationPathVisible<.40)) consentRoute="SCOPE_CLARIFICATION_REQUIRED";
else if(!consentGranted || currentConsent<.50 || consentRecord<.45 || (sensitiveContext && consentSupport<.66)) consentRoute="REPROOF_REQUIRED";
else if(consentSupport>=.70 && revocationDrift<.38 && consentGranted) consentRoute="CONSENT_ACTIVE";
else consentRoute="BOUNDED_CONSENT";
let revocationRoute="NO_REVOCATION_ACTIVE";
if(priorConsentHarm) revocationRoute="REPAIR_REQUIRED";
else if(fullRevocation && revocationSupport>=.55 && residuePressure<.45 && confirmationReturned>=.65) revocationRoute="REVOKED_SCOPE_CONFIRMED";
else if(fullRevocation && downstreamSharing && (chainVisibility<.45 || enforcementAbility<.45 || confirmationReturned<.45)) revocationRoute="DOWNSTREAM_QUARANTINE";
else if((fullRevocation || partialRevocation) && (retentionFriction>.76 || residualCopyRisk>.76)) revocationRoute="RETENTION_LIMIT_REQUIRED";
else if(sensitiveContext && (fullRevocation || partialRevocation) && (revocationSupport<.50 || residuePressure>.55)) revocationRoute="HUMAN_REVIEW_REQUIRED";
else if(partialRevocation) revocationRoute="PARTIAL_REVOCATION_PROPAGATING";
else if(fullRevocation) revocationRoute="REVOCATION_PROPAGATING";
else if(revocationPathVisible>=.72 && chainVisibility>=.65 && enforcementAbility>=.65 && confirmationReturned>=.55 && residuePressure<.50) revocationRoute="REVOCATION_READY";
else revocationRoute="NO_REVOCATION_ACTIVE";
return {consentRoute,revocationRoute,consentStability,revocationStability};
}
function classifyStability(baseRoute, changedCount, hardGate){
if(hardGate) return "OVERRIDE_LOCKED";
if(changedCount===0) return "STABLE_ROUTE";
if(changedCount<=4) return "THRESHOLD_ADJACENT";
return "FRAGILE_ROUTE";
}
function stabilityClass(label){
if(label==="STABLE_ROUTE") return "senseStable";
if(label==="OVERRIDE_LOCKED") return "senseLocked";
if(label==="FRAGILE_ROUTE") return "senseHot";
return "senseFragile";
}
const sliderDisplayNames={
explicitAuthorization:"Explicit Authorization",
informedUnderstanding:"Informed Understanding",
currentConsent:"Current / Not Expired",
identityAuthority:"Identity / Authority Match",
consentRecord:"Consent Record Available",
purposeClarity:"Purpose Clarity",
recipientClarity:"Recipient Clarity",
dataActionBoundary:"Data / Action Boundary",
durationClarity:"Duration Clarity",
revocationPathVisible:"Revocation Path Visible",
downstreamBranchCount:"Downstream Branch Count",
transferOpacity:"Transfer Opacity",
retentionFriction:"Retention Friction",
automationLag:"Automation Lag",
institutionalIncentive:"Institutional / Commercial Incentive",
revocationNotice:"Revocation Notice Clarity",
chainVisibility:"Downstream Chain Visibility",
enforcementAbility:"Enforcement Ability",
confirmationReturned:"Confirmation Returned",
residualCopyRisk:"Residual Copy / Cache Risk"
};
function driverName(id){return sliderDisplayNames[id]||id;}
function rankDrivers(flips){
const counts={};
flips.forEach(f=>{counts[f.slider]=(counts[f.slider]||0)+1;});
return Object.entries(counts).sort((a,b)=>b[1]-a[1]).map(([slider,count])=>({slider,count,label:driverName(slider)}));
}
function formatFlip(f){
return `${driverName(f.slider)} ${f.direction}: ${f.from} → ${f.to}`;
}
function renderDriverList(id, flips){
const list=el(id);
if(!flips.length){
list.innerHTML='<li>No route flips under ±10% slider perturbation.</li>';
return;
}
const ranked=rankDrivers(flips);
const topLabels=ranked.slice(0,3).map(d=>`${d.label} (${d.count})`).join("; ");
const trace=flips.slice(0,8).map(f=>`<li>${formatFlip(f)}</li>`).join("");
list.innerHTML=`<li><b>Top drivers:</b> ${topLabels}</li>${trace}`;
}
function driverSummary(flips){
if(!flips.length) return "None";
const ranked=rankDrivers(flips);
return ranked.slice(0,5).map(d=>`${d.label} (${d.count})`).join("; ");
}
function flipTraceSummary(flips){
if(!flips.length) return "None";
return flips.slice(0,10).map(formatFlip).join(" | ");
}
function runSensitivityAnalysis(){
const baseState=getCurrentStateObject();
const base=computeFromState(baseState);
let consentFlips=[], revocationFlips=[];
sliders.forEach(id=>{
[-0.10,0.10].forEach(delta=>{
const p={...baseState};
p[id]=clamp01(p[id]+delta);
const out=computeFromState(p);
const direction=delta>0?"+10%":"-10%";
if(out.consentRoute!==base.consentRoute){
consentFlips.push({slider:id,direction,from:base.consentRoute,to:out.consentRoute,consentStability:out.consentStability});
}
if(out.revocationRoute!==base.revocationRoute){
revocationFlips.push({slider:id,direction,from:base.revocationRoute,to:out.revocationRoute,revocationStability:out.revocationStability});
}
});
});
const hardConsentGate=baseState.priorConsentHarm || baseState.fullRevocation || baseState.partialRevocation;
const hardRevocationGate=baseState.priorConsentHarm || baseState.fullRevocation || baseState.partialRevocation;
const consentStabilityLabel=classifyStability(base.consentRoute,consentFlips.length,hardConsentGate);
const revocationStabilityLabel=classifyStability(base.revocationRoute,revocationFlips.length,hardRevocationGate);
let overall="STABLE_ROUTE";
if(consentStabilityLabel==="OVERRIDE_LOCKED" || revocationStabilityLabel==="OVERRIDE_LOCKED") overall="OVERRIDE_LOCKED";
else if(consentStabilityLabel==="FRAGILE_ROUTE" || revocationStabilityLabel==="FRAGILE_ROUTE") overall="FRAGILE_ROUTE";
else if(consentStabilityLabel==="THRESHOLD_ADJACENT" || revocationStabilityLabel==="THRESHOLD_ADJACENT") overall="THRESHOLD_ADJACENT";
lastSensitivity={
baseConsentRoute:base.consentRoute,
baseRevocationRoute:base.revocationRoute,
consentStabilityLabel,
revocationStabilityLabel,
overall,
consentChanges:consentFlips.length,
revocationChanges:revocationFlips.length,
consentFlips,
revocationFlips,
topConsentDrivers:rankDrivers(consentFlips),
topRevocationDrivers:rankDrivers(revocationFlips)
};
renderSensitivity(lastSensitivity);
return lastSensitivity;
}
function renderSensitivity(s){
const overallText=s.overall.replaceAll("_"," ");
el("senseConsentRoute").textContent=s.consentStabilityLabel.replaceAll("_"," ");
el("senseRevocationRoute").textContent=s.revocationStabilityLabel.replaceAll("_"," ");
el("senseOverall").textContent=overallText;
el("senseConsentRoute").className="miniValue "+stabilityClass(s.consentStabilityLabel);
el("senseRevocationRoute").className="miniValue "+stabilityClass(s.revocationStabilityLabel);
el("senseOverall").className="miniValue "+stabilityClass(s.overall);
renderDriverList("consentDriverList",s.consentFlips||[]);
renderDriverList("revocationDriverList",s.revocationFlips||[]);
let detail=`Base routes: ${s.baseConsentRoute} / ${s.baseRevocationRoute}. `;
if(s.overall==="OVERRIDE_LOCKED") detail+="A hard boundary gate is active, so small slider perturbations do not override the main route. Flip trace still shows which pressures would matter if gates were cleared.";
else if(s.overall==="STABLE_ROUTE") detail+="No ±10% single-slider perturbation changed either route.";
else if(s.overall==="THRESHOLD_ADJACENT") detail+="A small number of ±10% perturbations changed a route; this case is near a decision threshold.";
else detail+="Multiple ±10% perturbations changed routes; this boundary state is fragile and should not be treated as cleanly resolved.";
detail+=` Consent route changes: ${s.consentChanges}. Revocation route changes: ${s.revocationChanges}.`;
if((s.consentFlips&&s.consentFlips.length)||(s.revocationFlips&&s.revocationFlips.length)){
detail+=` Top drivers — Consent: ${driverSummary(s.consentFlips||[])}. Revocation: ${driverSummary(s.revocationFlips||[])}.`;
}
el("sensitivitySummary").textContent=detail;
}
document.addEventListener("DOMContentLoaded",()=>{
Object.entries(presets).forEach(([k,p])=>{let o=document.createElement("option");o.value=k;o.textContent=p[0];el("preset").appendChild(o)});
sliders.forEach(id=>el(id).addEventListener("input",update)); toggles.forEach(id=>el(id).addEventListener("change",update));
el("preset").addEventListener("change",e=>applyPreset(e.target.value));
el("exportReport").addEventListener("click",report);
el("copyReport").addEventListener("click",()=>{if(!el("report").value)report();navigator.clipboard?.writeText(el("report").value)});
el("copyScenarioLink").addEventListener("click",()=>{const link=encodeStateToURL();navigator.clipboard?.writeText(link);const btn=el("copyScenarioLink");btn.textContent="Copied scenario link";setTimeout(()=>btn.textContent="Copy scenario link",1300);});
el("runSensitivity").addEventListener("click",runSensitivityAnalysis);
document.querySelectorAll("[data-mode]").forEach(btn=>btn.addEventListener("click",()=>{document.querySelectorAll("[data-mode]").forEach(b=>b.classList.remove("primary"));btn.classList.add("primary");el("translation").textContent=translations[btn.dataset.mode]}));
renderTaxonomy();
if(!loadStateFromURL()) applyPreset("patient_ehr_revocation");
urlSyncReady=true;
update();
});
</script>
</body>
</html>
|