From 5f2c3e9612d6e910e59d2fdbadbcb10860552e75 Mon Sep 17 00:00:00 2001 From: alixthegreat <146326639+alixxhiscock@users.noreply.github.com> Date: Sat, 11 Apr 2026 16:52:48 +0100 Subject: [PATCH 1/4] Add implementation plan for sis configuration threshold --- ...iguration_threshold_implementation_plan.md | 247 ++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 planning_notes/sis-configuration-threshold/sis_configuration_threshold_implementation_plan.md diff --git a/planning_notes/sis-configuration-threshold/sis_configuration_threshold_implementation_plan.md b/planning_notes/sis-configuration-threshold/sis_configuration_threshold_implementation_plan.md new file mode 100644 index 00000000..3254e989 --- /dev/null +++ b/planning_notes/sis-configuration-threshold/sis_configuration_threshold_implementation_plan.md @@ -0,0 +1,247 @@ +# SIS Configuration Threshold Display (Scenario 2) Implementation Plan + +Date: 2026-04-11 +Owner: Break Escape engineering +Scope: Implement the Scenario 2 SIS configuration threshold minigame with minimal code changes and full narrative/state integration. + +## 1. Source Alignment and Scope + +This plan is aligned to: + +- planning_notes/sis_scenarios/case_2_energy_game_design/minigame_planning.md +- planning_notes/sis_scenarios/case_2_energy_game_design/development_tasks.csv +- planning_notes/sis_scenarios/case_2_energy_game_design/case_2_energy_gdd.md +- planning_notes/sis_scenarios/case_2_energy_game_design/new_objects_planning.md +- planning_notes/sis_scenarios/case_2_energy_information_pack/requirements/claims.md +- scenarios/sis02_energy/scenario.json.erb +- scenarios/sis02_energy/mission.json +- scenarios/sis02_energy/ink/INK_DEVELOPMENT_SUMMARY.md + +Note on path request: + +- scenarios/sis02_healthcare does not exist in this repo. +- Scenario 2 implementation target is scenarios/sis02_energy. + +## 2. Canonical Feature Definition + +Feature name: SIS Configuration Threshold Display + +Intended behavior: + +- Interactive minigame opened from the Engineering Workshop SIS panel object. +- Shows a setpoint table: parameter, current value, certified baseline, deviation status, last-modified, modified-by. +- Deviation rows are highlighted and expandable for explanatory detail. +- Compare workflow is delivered in a stub-compatible way in phase 1 (no new globals), with stricter certification gating deferred to phase 2 if needed. +- Confirm action records tamper confirmation and advances narrative logic. + +Core teaching moment: + +- THERMAL_RUNAWAY_THRESHOLD changed from 55C certified baseline to 85C current value. + +## 3. Known Naming Drift (Must Resolve Before Coding) + +There is MG ID drift across docs: + +- minigame_planning labels SIS display as MG-03. +- development_tasks labels SIS display as MG-02. + +Implementation rule: + +- Use feature/object identity, not MG number: + - object id: sis_config_panel + - behavior: SIS Configuration Threshold Display + +There is variable naming drift across docs: + +- sis_certification_seen +- sis_cert_reviewed +- current scenario uses sis_config_seen and sets sis_tamper_confirmed from document read. + +Scenario 2 stub precedence rule (source of truth for implementation phase 1): + +- Keep existing globals and placeholder flow already defined in scenarios/sis02_energy/scenario.json.erb. +- Do not introduce new global variable names in phase 1. +- Use current globals as implemented in scenario stub: + - sis_config_seen + - sis_tamper_confirmed + - en002_claim_assessed + +Variable governance rule (authoritative for implementation): + +- planning_notes/sis_scenarios/case_2_energy_game_design defines intended gameplay concepts. +- scenarios/sis02_energy/scenario.json.erb defines authoritative current variable names for implementation. +- If a new global variable becomes necessary, request user approval before adding it. + +Optional phase 2 cleanup: + +- Add a dedicated certification-reviewed global only if needed after phase 1 is stable. + +## 4. Dependency Map + +Scenario objects: + +- sis_config_panel (engineering workshop): launch point for minigame +- SIS certification document object (filing cabinet): enables compare workflow + +Global variables read: + +- sis_config_seen (existing open/read marker) +- sis_tamper_confirmed (existing progression marker; also for reopen idempotency) + +Global variables written: + +- sis_config_seen +- sis_tamper_confirmed +- en002_claim_assessed + +Narrative and progression dependencies: + +- Priya branch unlocks on sis_tamper_confirmed +- Dr Bashir debrief logic references sis_tamper_confirmed +- Alarm panel state logic references sis_tamper_confirmed (current or future engine behavior) +- Objectives/tasks tied to SIS investigation complete through existing event mappings and globals + +Claims linkage: + +- EN-002 is the primary claim teaching link for this minigame. + +## 5. Minimal Technical Implementation Plan + +### 5.1 Files to Add + +1. public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js + +- New minigame class extending MinigameScene. +- Render table UI in DOM/CSS (consistent with existing HTML/CSS minigame patterns). +- Read scenario params for table rows and labels. +- Emit state updates through existing global variable update pathway. + +2. public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold.css (optional) + +- Keep inline styles only if consistent with local minigame style patterns. +- Prefer scoped CSS class names if using style injection in JS. + +### 5.2 Files to Update + +1. public/break_escape/js/minigames/index.js + +- Import/export/register scene name: sis-config-threshold. + +2. public/break_escape/js/systems/interactions.js + +- Add object type handler for sis_config_panel (or explicit id fallback). +- Start minigame via MinigameFramework.startMinigame with scenarioData payload. + +3. scenarios/sis02_energy/scenario.json.erb + +- Convert current readable placeholder for sis_config_panel into minigame-backed object config. +- Preserve existing stub globals and placeholder naming. +- Build minigame behavior from existing sis_config_panel stub content (same setpoints, same narrative text intent). +- Keep certification-document interaction compatible with current scenario flow in phase 1. +- Only adjust global writes when required to avoid bypassing the minigame, and keep objective/NPC behavior unchanged. + +### 5.3 Data Contract (scenarioData) + +Suggested shape: + +- type: sis_config_panel +- title: SIS CONFIGURATION - BATTERY HALL SIS +- rows: [] + - parameter + - currentValue + - certifiedValue + - status (GREEN/AMBER/RED) + - lastModified + - modifiedBy + - detailText +- compare: + - requiresGlobal: sis_tamper_confirmed (phase 1 fallback to existing globals only) + - disabledHint +- actions: + - confirmSets: + - sis_tamper_confirmed: true + - en002_claim_assessed: true + - openSets: + - sis_config_seen: true + +## 6. UI/Visual Design Specification + +Style direction: + +- Industrial SIS panel UI, small-control-system feel, no rounded corners. +- Use strong table hierarchy and high-contrast status highlights. + +Visual requirements: + +- Header: SIS configuration title bar. +- Main: tabular setpoint grid with clear certified vs current columns. +- Deviation rows: amber/red emphasis with warning glyph. +- Expanded explanation panel/modal for clicked deviation row. +- Compare button: + - phase 1: use existing stub-compatible behavior (no new certification-reviewed global) + - phase 2 (optional): add strict certification-reviewed gating +- Confirm button: + - explicit irreversible confirmation dialog + - completion state shown before close + +Animation/motion (minimal): + +- subtle row highlight pulse for deviated row +- panel slide/fade for compare overlay +- no excessive animation + +Accessibility/readability: + +- ensure legible type size for desktop and wall-monitor use +- color is not the sole signal; include status text labels + +## 7. Integration Behavior (End-to-End) + +1. Player opens sis_config_panel. +2. Minigame opens and sets sis_config_seen=true. +3. Player inspects rows and can expand deviation details. +4. Compare feature follows phase 1 stub-compatible behavior (strict cert-review gating deferred unless a dedicated global is introduced later). +5. Player confirms tamper. +6. System sets sis_tamper_confirmed=true and en002_claim_assessed=true. +7. Existing event mappings/dialogue/objective logic progress without additional custom engine work. + +Implementation note for phase 1: + +- Since no dedicated certification-reviewed global currently exists in scenario stub, comparison gating must be implemented without adding new globals (or deferred while keeping the rest of the minigame fully functional). + +## 8. Acceptance Criteria + +Functional: + +- Minigame launches from sis_config_panel interaction. +- Correct setpoint values are displayed, including 55C vs 85C thermal threshold mismatch. +- Compare workflow behaves consistently with phase 1 stub constraints and does not require new globals. +- Confirm action updates required globals once, idempotently. +- Existing scenario placeholder globals remain the authority for progression. + +Narrative/system: + +- Priya and Dr Bashir SIS branches unlock as expected after confirm. +- No regression to objective progression. +- Alarm panel/SIS status references remain coherent with sis_tamper_confirmed. + +Quality/minimality: + +- No new engine subsystem introduced. +- Changes limited to minigame registration, interaction routing, and scenario object/global wiring. + +## 9. Implementation Order + +1. Build minigame scene directly from current sis_config_panel placeholder values/content. +2. Register and wire interaction handler. +3. Update scenario object to launch minigame while preserving existing Scenario 2 globals. +4. Remove or narrow only the minimum placeholder bypass logic necessary. +5. Run targeted Scenario 2 SIS progression test (objectives + Priya/Dr Bashir branches). + +## 10. Out of Scope for This Implementation + +- New timer engines, alarm panel driver engines, or hardware GPIO behavior. +- Reworking unrelated MG IDs across all planning docs. +- Large UI framework introduction. + +This keeps implementation minimal while fully delivering the SIS threshold discovery teaching objective for Scenario 2. From 980bc2c091e049ed6cae255e640c8dba265d2178 Mon Sep 17 00:00:00 2001 From: alixthegreat <146326639+alixxhiscock@users.noreply.github.com> Date: Sat, 11 Apr 2026 18:10:48 +0100 Subject: [PATCH 2/4] Add sis configuration threshold minigame --- app/views/break_escape/games/show.html.erb | 1 + index.html | 1 + .../css/sis-config-threshold-minigame.css | 265 ++++++++++++++++ public/break_escape/js/minigames/index.js | 4 + .../sis-config-threshold-minigame.js | 288 ++++++++++++++++++ .../break_escape/js/systems/interactions.js | 14 + scenarios/sis02_energy/scenario.json.erb | 43 ++- 7 files changed, 611 insertions(+), 5 deletions(-) create mode 100644 public/break_escape/css/sis-config-threshold-minigame.css create mode 100644 public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js diff --git a/app/views/break_escape/games/show.html.erb b/app/views/break_escape/games/show.html.erb index 3ecbf67a..38c683a6 100644 --- a/app/views/break_escape/games/show.html.erb +++ b/app/views/break_escape/games/show.html.erb @@ -43,6 +43,7 @@ + diff --git a/index.html b/index.html index a2452022..29982fea 100644 --- a/index.html +++ b/index.html @@ -51,6 +51,7 @@ + diff --git a/public/break_escape/css/sis-config-threshold-minigame.css b/public/break_escape/css/sis-config-threshold-minigame.css new file mode 100644 index 00000000..83faba21 --- /dev/null +++ b/public/break_escape/css/sis-config-threshold-minigame.css @@ -0,0 +1,265 @@ +/* SIS Config Threshold Minigame Styles */ + +.sis-threshold-minigame-container { + background: rgba(13, 20, 32, 0.72); + border: 2px solid #2b3446; +} + +.sis-threshold-minigame-game-container { + max-width: 620px; + margin: 20px auto; + width: 100%; + background: rgba(17, 26, 40, 0.78) !important; + border: 2px solid #263246; + box-shadow: inset 0 0 18px rgba(0, 0, 0, 0.5); + padding: 12px; + height: 50vh; + box-sizing: border-box; + display: flex; + align-items: center; +} + +.sis-threshold { + font-family: 'VT323', 'Courier New', monospace; + color: #e9edf5; + padding: 8px; + width: 100%; + min-height: 100%; + position: relative; + box-sizing: border-box; + display: flex; + flex-direction: column; + justify-content: center; +} + +.sis-threshold-header { + background: #133f73; + border: 2px solid #2b5d93; + padding: 10px 12px; + font-size: 18px; + letter-spacing: 0.4px; +} + +.sis-threshold-table { + width: 100%; + border-collapse: collapse; + margin-top: 10px; + font-size: 16px; +} + +.sis-threshold-table th, +.sis-threshold-table td { + border: 2px solid #2a2f3b; + padding: 8px; + text-align: left; + vertical-align: top; +} + +.sis-threshold-table th { + background: #1f2b3f; + color: #e9edf5; +} + +.sis-row { + background: #212733; + cursor: default; +} + +.sis-row-clickable { + cursor: pointer; +} + +.sis-row-clickable:hover { + background: #313744; +} + +.sis-status-green { + color: #84d78a; + font-weight: 700; +} + +.sis-status-amber { + color: #ffd14b; + font-weight: 700; +} + +.sis-status-red { + color: #ff6a6a; + font-weight: 700; +} + +.sis-row-meta { + color: #9ea7b7; + font-size: 14px; + margin-top: 4px; +} + +.sis-actions { + display: flex; + gap: 10px; + margin-top: 14px; + flex-wrap: wrap; +} + +.sis-btn { + border: 4px solid #2980b9; + border-radius: 0; + padding: 10px 20px; + font-family: 'VT323', monospace; + font-size: 18px; + cursor: pointer; + background: #3498db; + color: #fff; + transition: background 0.3s, box-shadow 0.2s ease, transform 0.15s ease, filter 0.2s ease; +} + +.sis-btn-compare { + background: #b88d21; + color: #fff; + border-color: #8d6f1c; +} + +.sis-btn-compare[disabled] { + background: #5d5d5d; + color: #cfcfcf; + cursor: not-allowed; +} + +.sis-btn-confirm { + background: #b32424; + color: #fff; + border-color: #7e1a1a; +} + +.sis-btn-compare:hover { + box-shadow: 0 0 12px rgba(255, 209, 75, 0.45); + filter: brightness(1.08); + transform: translateY(-1px); +} + +.sis-btn-confirm:hover { + background: #8f1f1f; + border-color: #6d1616; + box-shadow: none; + filter: none; + transform: none; +} + +.sis-btn:active { + transform: translateY(0); + background: #21618c; +} + +.sis-btn-confirm:active { + background: #731818; + border-color: #5d1212; +} + +.sis-help { + margin-top: 8px; + color: #c9cfd9; + font-size: 14px; +} + +.sis-overlay { + position: absolute; + inset: 0; + background: rgba(6, 10, 18, 0.32); + display: none; + align-items: center; + justify-content: center; + padding: 8px; + z-index: 5; + box-sizing: border-box; +} + +.sis-overlay.show { + display: flex; +} + +.sis-modal { + background: #161c26; + border: 2px solid #3c4d68; + max-width: 100%; + width: 100%; + padding: 14px; + box-sizing: border-box; + margin: 0 auto; +} + +.sis-modal h4 { + margin: 0 0 8px 0; + color: #f4f7fb; + font-size: 20px; +} + +.sis-modal p { + margin: 0; + color: #d3dae5; + font-size: 16px; + line-height: 1.35; +} + +.sis-modal-actions { + margin-top: 12px; + display: flex; + gap: 8px; + justify-content: flex-end; +} + +/* Match popup action buttons to framework minigame-button style exactly */ +.sis-modal-actions .sis-btn:not(.sis-btn-compare):not(.sis-btn-confirm) { + background: #3498db; + color: white; + border: 4px solid #2980b9; + padding: 10px 20px; + font-family: 'VT323', monospace; + font-size: 18px; +} + +.sis-modal-actions .sis-btn:not(.sis-btn-compare):not(.sis-btn-confirm):hover { + background: #2980b9; + box-shadow: none; + filter: none; + transform: none; +} + +.sis-modal-actions .sis-btn:not(.sis-btn-compare):not(.sis-btn-confirm):active { + background: #21618c; + transform: none; +} + +.sis-compare-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; + margin-top: 8px; + align-items: stretch; +} + +.sis-compare-card { + border: 2px solid #2b3443; + background: #202735; + padding: 10px; + min-height: 132px; + box-sizing: border-box; +} + +.sis-compare-card h5 { + margin: 0 0 6px 0; + font-size: 18px; + color: #f4f7fb; +} + +.sis-compare-item { + margin: 0 0 6px 0; + font-size: 17px; + line-height: 1.35; + color: #eef3ff; + word-break: break-word; +} + +.sis-compare-item-alert { + color: #ffd46b; + font-weight: 700; +} diff --git a/public/break_escape/js/minigames/index.js b/public/break_escape/js/minigames/index.js index 505204c4..46a9b9d8 100644 --- a/public/break_escape/js/minigames/index.js +++ b/public/break_escape/js/minigames/index.js @@ -25,6 +25,7 @@ export { EhrTerminalMinigame } from './ehr-terminal/ehr-terminal-minigame.js'; export { BackupRecoveryMinigame } from './backup-recovery/backup-recovery-minigame.js'; export { CommandBoardMinigame } from './command-board/command-board-minigame.js'; export { InfusionPumpMinigame } from './infusion-pump/infusion-pump-minigame.js'; +export { SisConfigThresholdMinigame, startSisConfigThresholdMinigame } from './sis-config-threshold/sis-config-threshold-minigame.js'; // Initialize the global minigame framework for backward compatibility import { MinigameFramework } from './framework/minigame-manager.js'; @@ -95,6 +96,7 @@ import { SiemDashboardMinigame } from './siem/siem-dashboard-minigame.js'; import { EhrTerminalMinigame } from './ehr-terminal/ehr-terminal-minigame.js'; import { CommandBoardMinigame } from './command-board/command-board-minigame.js'; import { InfusionPumpMinigame } from './infusion-pump/infusion-pump-minigame.js'; +import { SisConfigThresholdMinigame, startSisConfigThresholdMinigame } from './sis-config-threshold/sis-config-threshold-minigame.js'; // Import ransomware display minigame import { RansomwareDisplayMinigame } from './ransomware-display/ransomware-display-minigame.js'; @@ -127,6 +129,7 @@ MinigameFramework.registerScene('ehr-terminal', EhrTerminalMinigame); MinigameFramework.registerScene('backup-recovery', BackupRecoveryMinigame); MinigameFramework.registerScene('command-board', CommandBoardMinigame); MinigameFramework.registerScene('infusion-pump', InfusionPumpMinigame); +MinigameFramework.registerScene('sis-config-threshold', SisConfigThresholdMinigame); // Make minigame functions available globally window.startNotesMinigame = startNotesMinigame; @@ -143,3 +146,4 @@ window.startTitleScreenMinigame = startTitleScreenMinigame; window.startRFIDMinigame = startRFIDMinigame; window.returnToConversationAfterRFID = returnToConversationAfterRFID; window.startNetworkSegmentationMapMinigame = startNetworkSegmentationMapMinigame; +window.startSisConfigThresholdMinigame = startSisConfigThresholdMinigame; diff --git a/public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js b/public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js new file mode 100644 index 00000000..067f1113 --- /dev/null +++ b/public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js @@ -0,0 +1,288 @@ +import { MinigameScene } from '../framework/base-minigame.js'; + +const DEFAULT_ROWS = [ + { + parameter: 'THERMAL_RUNAWAY_THRESHOLD', + currentValue: '85C', + certifiedValue: '55C', + status: 'AMBER', + lastModified: '03:22 (today)', + modifiedBy: 'engineering_access', + detailText: 'This value deviates from the IEC 61511 certified baseline. Possible causes: (1) authorised maintenance change requiring recertification, (2) unauthorised modification.' + }, + { + parameter: 'H2_ALARM_THRESHOLD', + currentValue: '1.2% LEL', + certifiedValue: '1.0% LEL', + status: 'AMBER', + lastModified: '03:22 (today)', + modifiedBy: 'engineering_access', + detailText: 'Hydrogen trip threshold has been raised above certified baseline, reducing early-warning margin.' + }, + { + parameter: 'MAX_CHARGE_VOLTAGE', + currentValue: '4.32 V/cell', + certifiedValue: '4.25 V/cell', + status: 'AMBER', + lastModified: '03:22 (today)', + modifiedBy: 'engineering_access', + detailText: 'Overcharge protection limit exceeds certified value, increasing thermal risk during charging.' + } +]; + +const CERTIFICATION_DOC_NAME = 'SIS Certification Document (IEC 61511)'; + +function normalizeStatus(status) { + return String(status || 'GREEN').toUpperCase(); +} + +export class SisConfigThresholdMinigame extends MinigameScene { + constructor(container, params = {}) { + super(container, params); + this.rows = Array.isArray(params.rows) && params.rows.length > 0 ? params.rows : DEFAULT_ROWS; + this.compareTitle = params.compareTitle || 'Certification Comparison'; + this.confirmLabel = params.confirmLabel || 'Confirm SIS Tamper - Report to Security'; + } + + init() { + this.params.title = this.params.title || 'SIS Configuration - Battery Hall SIS'; + this.params.cancelText = this.params.cancelText || 'Close'; + super.init(); + + // Keep SIS panel footprint similar to PIN minigame and style as translucent panel. + this.container.className += ' sis-threshold-minigame-container'; + this.gameContainer.className += ' sis-threshold-minigame-game-container'; + + this.setScenarioGlobal('sis_config_seen', true); + this.render(); + this.bindEvents(); + } + + render() { + const container = document.createElement('div'); + container.className = 'sis-threshold'; + + const canCompare = this.hasCertificationDoc(); + + const tableRows = this.rows.map((row, index) => { + const status = normalizeStatus(row.status); + const statusClass = status === 'RED' ? 'sis-status-red' : status === 'AMBER' ? 'sis-status-amber' : 'sis-status-green'; + const clickableClass = status === 'GREEN' ? '' : 'sis-row-clickable'; + + return ` + + +
${row.parameter || ''}
+
${row.lastModified || ''} | ${row.modifiedBy || ''}
+ + ${row.currentValue || ''} + ${status} + + `; + }).join(''); + + container.innerHTML = ` +
SIS CONFIGURATION - BATTERY HALL SIS
+ + + + + + + + + + ${tableRows} + +
ParameterCurrent ValueStatus
+
+ + +
+
+ ${canCompare ? 'Select highlighted rows to inspect deviations.' : 'Retrieve the SIS certification document to unlock side-by-side comparison.'} +
+
+ `; + + this.gameContainer.appendChild(container); + + this.overlayEl = container.querySelector('#sis-detail-overlay'); + this.helpTextEl = container.querySelector('#sis-help-text'); + } + + bindEvents() { + this.gameContainer.querySelectorAll('tr[data-clickable="true"]').forEach((rowEl) => { + this.addEventListener(rowEl, 'click', () => { + const index = Number(rowEl.getAttribute('data-row-index')); + const row = this.rows[index]; + this.showDetail(row); + }); + }); + + const compareBtn = this.gameContainer.querySelector('#sis-compare-btn'); + if (compareBtn) { + this.addEventListener(compareBtn, 'click', () => { + if (!this.hasCertificationDoc()) return; + this.showCompare(); + }); + } + + const confirmBtn = this.gameContainer.querySelector('#sis-confirm-btn'); + if (confirmBtn) { + this.addEventListener(confirmBtn, 'click', () => { + this.showConfirm(); + }); + } + } + + hasCertificationDoc() { + const items = window.inventory?.items || []; + const hasInventoryDoc = items.some((item) => { + const name = item?.scenarioData?.name || item?.name || ''; + return String(name).trim() === CERTIFICATION_DOC_NAME; + }); + + if (hasInventoryDoc) return true; + + const notes = window.gameState?.notes || []; + return notes.some((note) => String(note?.title || '').trim() === CERTIFICATION_DOC_NAME); + } + + showDetail(row) { + if (!this.overlayEl || !row) return; + + const detailText = row.detailText || 'This value deviates from the IEC 61511 certified baseline.'; + this.overlayEl.innerHTML = ` +
+

${row.parameter || 'Parameter Detail'}

+

${detailText}

+
+ +
+
+ `; + this.overlayEl.classList.add('show'); + + const closeBtn = this.overlayEl.querySelector('#sis-detail-close'); + this.addEventListener(closeBtn, 'click', () => this.closeOverlay()); + } + + showCompare() { + if (!this.overlayEl) return; + + const currentRows = this.rows.map((row) => { + const status = normalizeStatus(row.status); + const className = status === 'GREEN' ? 'sis-compare-item' : 'sis-compare-item sis-compare-item-alert'; + return `
${row.parameter}: ${row.currentValue}
`; + }).join(''); + + const certifiedRows = this.rows.map((row) => { + return `
${row.parameter}: ${row.certifiedValue}
`; + }).join(''); + + this.overlayEl.innerHTML = ` +
+

${this.compareTitle}

+
+
+
Current SIS Values
+ ${currentRows} +
+
+
Certified Reference (IEC 61511)
+ ${certifiedRows} +
+
+
+ +
+
+ `; + this.overlayEl.classList.add('show'); + + const closeBtn = this.overlayEl.querySelector('#sis-compare-close'); + this.addEventListener(closeBtn, 'click', () => this.closeOverlay()); + } + + showConfirm() { + if (!this.overlayEl) return; + + this.overlayEl.innerHTML = ` +
+

Confirm SIS Tamper Report

+

Report detected SIS setpoint deviations to security operations?

+
+ + +
+
+ `; + this.overlayEl.classList.add('show'); + + const noBtn = this.overlayEl.querySelector('#sis-confirm-no'); + const yesBtn = this.overlayEl.querySelector('#sis-confirm-yes'); + this.addEventListener(noBtn, 'click', () => this.closeOverlay()); + this.addEventListener(yesBtn, 'click', () => this.applyConfirm()); + } + + applyConfirm() { + this.setScenarioGlobal('sis_tamper_confirmed', true); + + this.gameResult = { + reported: true, + source: 'sis-config-threshold' + }; + + this.closeOverlay(); + this.showSuccess('SIS tamper reported. Priya has been notified.', true, 900); + } + + closeOverlay() { + if (!this.overlayEl) return; + this.overlayEl.classList.remove('show'); + this.overlayEl.innerHTML = ''; + } + + setScenarioGlobal(name, value) { + if (window.npcManager?.setGlobalVariable) { + window.npcManager.setGlobalVariable(name, value); + return; + } + + if (!window.gameState) window.gameState = {}; + if (!window.gameState.globalVariables) window.gameState.globalVariables = {}; + + const oldValue = window.gameState.globalVariables[name]; + window.gameState.globalVariables[name] = value; + + if (window.eventDispatcher) { + window.eventDispatcher.emit(`global_variable_changed:${name}`, { + name, + value, + oldValue + }); + } + } +} + +export function startSisConfigThresholdMinigame(sprite = null) { + if (!window.MinigameFramework) { + console.error('[SIS] MinigameFramework not available'); + return; + } + + const scenarioData = sprite?.scenarioData || {}; + const minigameData = scenarioData.minigame || {}; + + const params = { + title: minigameData.title || scenarioData.name || 'SIS Configuration Panel', + rows: Array.isArray(minigameData.rows) ? minigameData.rows : [], + compareTitle: minigameData.compareTitle || 'Compare with Certification Document', + confirmLabel: minigameData.confirmLabel || 'Confirm SIS Tamper - Report to Security' + }; + + window.MinigameFramework.startMinigame('sis-config-threshold', null, { + ...params + }); +} diff --git a/public/break_escape/js/systems/interactions.js b/public/break_escape/js/systems/interactions.js index 3ee5da59..29aa1940 100644 --- a/public/break_escape/js/systems/interactions.js +++ b/public/break_escape/js/systems/interactions.js @@ -965,6 +965,20 @@ export function handleObjectInteraction(sprite) { return; } + // Handle SIS configuration panel interaction + if (sprite.scenarioData.id === 'sis_config_panel' || + sprite.scenarioData.type === 'sis_config_panel' || + sprite.scenarioData.interactionType === 'sis_config_panel') { + console.log('SIS config panel interaction:', sprite.scenarioData); + + if (window.startSisConfigThresholdMinigame) { + window.startSisConfigThresholdMinigame(sprite); + } else { + window.gameAlert('SIS configuration minigame unavailable.', 'error', 'Error', 3000); + } + return; + } + // Handle the Lockpick Set - pick it up if takeable, or use it if in inventory if (sprite.scenarioData.type === "lockpick" || sprite.scenarioData.type === "lockpickset") { // If it's in inventory (marked as non-takeable), just acknowledge it diff --git a/scenarios/sis02_energy/scenario.json.erb b/scenarios/sis02_energy/scenario.json.erb index a53943f5..cbc8313e 100644 --- a/scenarios/sis02_energy/scenario.json.erb +++ b/scenarios/sis02_energy/scenario.json.erb @@ -36,11 +36,11 @@ # readable notes — analog thermometer, SIS config, NIS form # timedConversation — Priya opening briefing cutscene # NPC eventMappings — state-driven global variable progression +# sis_config_panel — interactive SIS threshold display (MG-03) # # PLACEHOLDER — functional stub in place; custom work pending # esd_pushbutton → lockType:pin (needs MG-01 custom ESD pushbutton interaction) # hmi_ops_01 → password PC (needs MG-02 SCADA historian trend viewer VM) -# sis_config_panel → readable text (needs MG-03 interactive SIS threshold display) # alarm_panel → smartscreen (needs ENG-01 state-reactive alarm panel driver) # hydrogen_detector → smartscreen (needs ENG-02 timed gas alarm progression) # @@ -296,7 +296,7 @@ hmi_password = @random_password # HMI-OPS-01 SCADA workstation password "order": 6, "tasks": [ { - // TODO[MINIGAME]: sis_config_panel — custom SIS threshold viewer MG-03; currently readable text + // IMPLEMENTED: sis_config_panel — custom SIS threshold viewer MG-03 "taskId": "read_sis_config", "title": "Read the SIS configuration panel — check thermal runaway threshold", "type": "manual", @@ -887,7 +887,7 @@ hmi_password = @random_password # HMI-OPS-01 SCADA workstation password "onRead": { "setVariable": { "jump_server_isolated": true } } }, - // TODO[MINIGAME]: sis_config_panel — interactive SIS threshold display — needs MG-03 + // IMPLEMENTED: sis_config_panel — interactive SIS threshold display (MG-03) // PLACEHOLDER: readable smartscreen showing tampered setpoints with amber deviation highlight { "type": "smartscreen", @@ -895,6 +895,40 @@ hmi_password = @random_password # HMI-OPS-01 SCADA workstation password "name": "SIS Configuration Panel", "takeable": false, "readable": true, + "minigame": { + "title": "SIS CONFIGURATION - BATTERY HALL SIS", + "compareTitle": "Compare with Certification Document", + "confirmLabel": "Confirm SIS Tamper - Report to Security", + "rows": [ + { + "parameter": "THERMAL_RUNAWAY_THRESHOLD", + "currentValue": "85C", + "certifiedValue": "55C", + "status": "AMBER", + "lastModified": "03:22 (today)", + "modifiedBy": "engineering_access", + "detailText": "This value deviates from the IEC 61511 certified baseline. Possible causes: (1) authorised maintenance change requiring recertification, (2) unauthorised modification." + }, + { + "parameter": "H2_ALARM_THRESHOLD", + "currentValue": "1.2% LEL", + "certifiedValue": "1.0% LEL", + "status": "AMBER", + "lastModified": "03:22 (today)", + "modifiedBy": "engineering_access", + "detailText": "Hydrogen trip threshold has been raised above certified baseline, reducing early-warning margin." + }, + { + "parameter": "MAX_CHARGE_VOLTAGE", + "currentValue": "4.32 V/cell", + "certifiedValue": "4.25 V/cell", + "status": "AMBER", + "lastModified": "03:22 (today)", + "modifiedBy": "engineering_access", + "detailText": "Overcharge protection limit exceeds certified value, increasing thermal risk during charging." + } + ] + }, "observations": "A dedicated display showing the SIS (Safety Instrumented System) configuration. The thermal runaway threshold value is highlighted in amber — a deviation indicator.", "text": "SAFETY INSTRUMENTED SYSTEM — CONFIGURATION\nAlbion Battery Hall SIS | Firmware: 2.4.1\n\nTHERMAL RUNAWAY PROTECTION:\n THERMAL_RUNAWAY_THRESHOLD: 85°C [AMBER — DEVIATION FROM CERTIFIED]\n Certified baseline: 55°C\n Last modified: 03:22 (today) | Modified by: engineering_access\n\nGAS DETECTION TRIP:\n H2_ALARM_THRESHOLD: 1.2% LEL [AMBER — DEVIATION FROM CERTIFIED]\n Certified baseline: 1.0% LEL\n\nOVERCHARGE PROTECTION:\n MAX_CHARGE_VOLTAGE: 4.32 V/cell [AMBER — DEVIATION FROM CERTIFIED]\n Certified baseline: 4.25 V/cell\n\nFIRMWARE VERSION: 2.4.1 (certified firmware: 2.3.8)\n Patch 2.4.1 status: UNAUTHORISED INSTALLATION\n Note: 2.4.1 was available from vendor for 18 months. Installation here was not via the approved recertification process.\n\nStatus: MULTIPLE SETPOINT DEVIATIONS DETECTED", "onRead": { "setVariable": { "sis_config_seen": true } } @@ -917,8 +951,7 @@ hmi_password = @random_password # HMI-OPS-01 SCADA workstation password "name": "SIS Certification Document (IEC 61511)", "readable": true, "takeable": true, - "text": "SAFETY REQUIREMENTS SPECIFICATION\nAlbion Energy Storage Ltd — Battery Hall SIS\nIEC 61511 SIL 2 Certification — Certified 18 months ago\n\nCERTIFIED OPERATING SETPOINTS (MANDATORY — DO NOT MODIFY WITHOUT RECERTIFICATION):\n THERMAL_RUNAWAY_THRESHOLD: 55°C\n H2_ALARM_THRESHOLD: 1.0% LEL\n MAX_CHARGE_VOLTAGE: 4.25 V/cell\n\nPATCH STATUS:\n SIS Firmware 2.4.1 — available from vendor for 18 months.\n Status: DEFERRED pending recertification budget approval.\n Recertification cost estimate: £180,000 | Duration: 8 weeks offline.\n Risk assessment status: Accepted — 'Low probability; maintain compensating controls.'\n\nANY MODIFICATION to SIS setpoints requires SIL 2 recertification under IEC 61511.\nA modified but uncertified SIS is not a SIS — it is an unvalidated control system.", - "onRead": { "setVariable": { "sis_tamper_confirmed": true } } + "text": "SAFETY REQUIREMENTS SPECIFICATION\nAlbion Energy Storage Ltd — Battery Hall SIS\nIEC 61511 SIL 2 Certification — Certified 18 months ago\n\nCERTIFIED OPERATING SETPOINTS (MANDATORY — DO NOT MODIFY WITHOUT RECERTIFICATION):\n THERMAL_RUNAWAY_THRESHOLD: 55°C\n H2_ALARM_THRESHOLD: 1.0% LEL\n MAX_CHARGE_VOLTAGE: 4.25 V/cell\n\nPATCH STATUS:\n SIS Firmware 2.4.1 — available from vendor for 18 months.\n Status: DEFERRED pending recertification budget approval.\n Recertification cost estimate: £180,000 | Duration: 8 weeks offline.\n Risk assessment status: Accepted — 'Low probability; maintain compensating controls.'\n\nANY MODIFICATION to SIS setpoints requires SIL 2 recertification under IEC 61511.\nA modified but uncertified SIS is not a SIS — it is an unvalidated control system." }, { "type": "notes2", From 50f7866f4567ba8caa429d0c8d7985ff7b9c5e62 Mon Sep 17 00:00:00 2001 From: alixthegreat <146326639+alixxhiscock@users.noreply.github.com> Date: Sun, 12 Apr 2026 14:53:48 +0100 Subject: [PATCH 3/4] fix merge conflict in interactions.js --- public/break_escape/js/systems/interactions.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/break_escape/js/systems/interactions.js b/public/break_escape/js/systems/interactions.js index 9e559b63..dffc81d8 100644 --- a/public/break_escape/js/systems/interactions.js +++ b/public/break_escape/js/systems/interactions.js @@ -975,6 +975,10 @@ export function handleObjectInteraction(sprite) { window.startSisConfigThresholdMinigame(sprite); } else { window.gameAlert('SIS configuration minigame unavailable.', 'error', 'Error', 3000); + } + return; + } + // Handle ESD pushbutton by object-type interaction if (sprite.scenarioData.type === 'esd_button' || sprite.scenarioData.interactionType === 'esd_button') { From 4bafb0bf20fed30f8f238e41786049c9a1a0255b Mon Sep 17 00:00:00 2001 From: alixthegreat <146326639+alixxhiscock@users.noreply.github.com> Date: Sun, 12 Apr 2026 16:01:06 +0100 Subject: [PATCH 4/4] Review of sis configuration threshold and minor changes --- .../sis_configuration_threshold_review.md | 116 ++++++++++++++++++ .../sis-config-threshold-minigame.js | 23 ++-- scenarios/sis02_energy/scenario.json.erb | 14 ++- 3 files changed, 138 insertions(+), 15 deletions(-) create mode 100644 planning_notes/sis-configuration-threshold/sis_configuration_threshold_review.md diff --git a/planning_notes/sis-configuration-threshold/sis_configuration_threshold_review.md b/planning_notes/sis-configuration-threshold/sis_configuration_threshold_review.md new file mode 100644 index 00000000..1cc860dc --- /dev/null +++ b/planning_notes/sis-configuration-threshold/sis_configuration_threshold_review.md @@ -0,0 +1,116 @@ +# SIS Configuration Threshold Review (2026-04-12) + +## Scope + +Review current SIS configuration threshold implementation against planning sources under `planning_notes/sis_scenarios`. + +## Planning Sources Reviewed + +- `planning_notes/sis_scenarios/case_2_energy_game_design/minigame_planning.md` +- `planning_notes/sis_scenarios/case_2_energy_game_design/development_tasks.csv` +- Supplemental implementation alignment notes: + - `planning_notes/sis-configuration-threshold/sis_configuration_threshold_implementation_plan.md` + +## Implementation Files Reviewed + +- `public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js` +- `public/break_escape/css/sis-config-threshold-minigame.css` +- `public/break_escape/js/minigames/index.js` +- `public/break_escape/js/systems/interactions.js` +- `scenarios/sis02_energy/scenario.json.erb` + +## Executive Summary + +Implementation is mostly in place and wired end-to-end (scene registration, object routing, UI, row-detail modal, compare overlay, and confirm flow). The prior certification-document bypass has been removed, and the progression now follows an explicit two-step model: review certification evidence, then confirm tamper. + +## Matches + +1. Minigame implemented as HTML/CSS interactive panel (planned MG-03/MG-02 equivalent). + - Evidence: `public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js` + - Evidence: `public/break_escape/css/sis-config-threshold-minigame.css` + - Planning: `planning_notes/sis_scenarios/case_2_energy_game_design/minigame_planning.md:87-99` + +2. Panel interaction is wired from `sis_config_panel` object and launches the SIS minigame. + - Evidence: `public/break_escape/js/systems/interactions.js:969-975` + - Evidence: `public/break_escape/js/minigames/index.js:137,155` + - Planning: `planning_notes/sis_scenarios/case_2_energy_game_design/minigame_planning.md:90` + +3. Scenario object carries minigame data contract (title, rows, compareTitle, confirmLabel). + - Evidence: `scenarios/sis02_energy/scenario.json.erb:1021-1029` + - Planning: `planning_notes/sis-configuration-threshold/sis_configuration_threshold_implementation_plan.md:146-166` + +4. Row-level detail expansion for non-GREEN rows is implemented. + - Evidence: `public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js:68-85,112-119,152-170` + - Planning: `planning_notes/sis_scenarios/case_2_energy_game_design/minigame_planning.md:102` + +5. Compare overlay exists and is disabled when certification evidence is missing. + - Evidence: `public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js:65,99,124-128,171-201` + - Planning: `planning_notes/sis_scenarios/case_2_energy_game_design/minigame_planning.md:103,116` + +6. Confirm action exists and sets `sis_tamper_confirmed = true`. + - Evidence: `public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js:229-231` + - Planning: `planning_notes/sis_scenarios/case_2_energy_game_design/minigame_planning.md:104` + +7. `sis_config_seen` is set when panel opens; objective task completion wiring is present. + - Evidence: `public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js:56` + - Evidence: `scenarios/sis02_energy/scenario.json.erb:517-521` + - Planning: `planning_notes/sis-configuration-threshold/sis_configuration_threshold_implementation_plan.md:88,93,177-179` + +## Mismatches + +No active functional mismatches identified for the implemented SIS minigame flow after the latest changes. + +### Resolved During Review + +1. Certification document read no longer bypasses tamper confirmation. + - Current behavior: cert document now sets `sis_certification_seen`, not `sis_tamper_confirmed`. + - Evidence: `scenarios/sis02_energy/scenario.json.erb:93,526,1098` + - Confirm remains explicit in minigame action: `public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js:234` + +2. Comparison gate now supports global-variable gating via `sis_certification_seen`. + - Evidence: `public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js:139` + - Compare gating now uses the global-variable check directly. + +3. `en002_claim_assessed` is intentionally mapped from confirmed tamper (`sis_tamper_confirmed`) via scenario event wiring. + - Evidence: `scenarios/sis02_energy/scenario.json.erb:533-536` + - Decision: accepted implementation approach. + +## Accepted Design Deviation + +1. Main table intentionally omits a visible "certified baseline" column so the certification document and compare action retain investigative value. + - Current behavior: certified values are shown in compare overlay, not in the default table. + - Evidence (current columns): `public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js:89-91` + - Evidence (certified values in compare overlay): `public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js:181` + - Status: accepted by design decision; no change required unless design intent changes. + +## Recommended Next Changes (Priority Order) + +1. Run end-to-end in-game validation of the intended sequence. + - Sequence to verify: `sis_config_seen` (panel read) -> `sis_certification_seen` (cert doc read) -> compare enabled -> `sis_tamper_confirmed` (confirm action) -> `en002_claim_assessed` event mapping. + - Evidence wiring: `scenarios/sis02_energy/scenario.json.erb:519,526,533-536`; `public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js:56,138,234`. + +## Follow-up Q&A Verification (2026-04-12) + +1. Is the cert-doc bypass still in place? + - No. Cert doc read now sets `sis_certification_seen` and no longer sets `sis_tamper_confirmed`: `scenarios/sis02_energy/scenario.json.erb:1098`. + +2. Why not show certified values in the base table? + - Accepted design rationale: keeping certified values in compare mode preserves the purpose of retrieving the certification document. + +3. Does `en002_claim_assessed` get set at all? + - Yes, indirectly. Event mapping on `global_variable_changed:sis_tamper_confirmed` sets `en002_claim_assessed = true`: `scenarios/sis02_energy/scenario.json.erb:533-536`. + - It is not set directly in `applyConfirm()`: `public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js:234`. + +4. Is there a `sis_certification_seen` or `sis_cert_reviewed` global set on cert-doc view? + - Yes. `sis_certification_seen` now exists in scenario globals and is set on cert-doc read. + - Evidence: `scenarios/sis02_energy/scenario.json.erb:93,1098`. + +## Current Assessment + +- Functional readiness: Good +- Design/spec conformance: Good +- Progression integrity risk: Low (pending final in-game validation) + +## Validation Status + +- End-to-end playthrough verification: Pending user run diff --git a/public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js b/public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js index 067f1113..55d55b48 100644 --- a/public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js +++ b/public/break_escape/js/minigames/sis-config-threshold/sis-config-threshold-minigame.js @@ -30,8 +30,6 @@ const DEFAULT_ROWS = [ } ]; -const CERTIFICATION_DOC_NAME = 'SIS Certification Document (IEC 61511)'; - function normalizeStatus(status) { return String(status || 'GREEN').toUpperCase(); } @@ -137,16 +135,16 @@ export class SisConfigThresholdMinigame extends MinigameScene { } hasCertificationDoc() { - const items = window.inventory?.items || []; - const hasInventoryDoc = items.some((item) => { - const name = item?.scenarioData?.name || item?.name || ''; - return String(name).trim() === CERTIFICATION_DOC_NAME; - }); + if (window.gameState?.globalVariables?.sis_certification_seen === true) { + return true; + } - if (hasInventoryDoc) return true; + const certTask = window.objectivesManager?.taskIndex?.find_certification_doc; + if (certTask?.status === 'completed') { + return true; + } - const notes = window.gameState?.notes || []; - return notes.some((note) => String(note?.title || '').trim() === CERTIFICATION_DOC_NAME); + return false; } showDetail(row) { @@ -247,7 +245,6 @@ export class SisConfigThresholdMinigame extends MinigameScene { setScenarioGlobal(name, value) { if (window.npcManager?.setGlobalVariable) { window.npcManager.setGlobalVariable(name, value); - return; } if (!window.gameState) window.gameState = {}; @@ -256,6 +253,10 @@ export class SisConfigThresholdMinigame extends MinigameScene { const oldValue = window.gameState.globalVariables[name]; window.gameState.globalVariables[name] = value; + if (window.npcConversationStateManager) { + window.npcConversationStateManager.broadcastGlobalVariableChange(name, value, null); + } + if (window.eventDispatcher) { window.eventDispatcher.emit(`global_variable_changed:${name}`, { name, diff --git a/scenarios/sis02_energy/scenario.json.erb b/scenarios/sis02_energy/scenario.json.erb index 4a281900..698d2891 100644 --- a/scenarios/sis02_energy/scenario.json.erb +++ b/scenarios/sis02_energy/scenario.json.erb @@ -90,6 +90,7 @@ hmi_password = @random_password # HMI-OPS-01 SCADA workstation password "network_isolated": false, "sis_config_seen": false, + "sis_certification_seen": false, "sis_tamper_confirmed": false, "en002_claim_assessed": false, @@ -521,11 +522,17 @@ hmi_password = @random_password # HMI-OPS-01 SCADA workstation password "completeTask": "read_sis_config" }, { - // find_certification_doc task completes and EN-002 claim assessed when cert doc confirms tamper + // find_certification_doc task completes when SIS certification evidence is reviewed + "eventPattern": "global_variable_changed:sis_certification_seen", + "condition": "value === true", + "onceOnly": true, + "completeTask": "find_certification_doc" + }, + { + // EN-002 claim assessed when player confirms SIS tamper "eventPattern": "global_variable_changed:sis_tamper_confirmed", "condition": "value === true", "onceOnly": true, - "completeTask": "find_certification_doc", "setGlobal": { "en002_claim_assessed": true } }, { @@ -1083,12 +1090,11 @@ hmi_password = @random_password # HMI-OPS-01 SCADA workstation password "name": "SIS Certification Document (IEC 61511)", "readable": true, "takeable": true, - "text": "SAFETY REQUIREMENTS SPECIFICATION\nAlbion Energy Storage Ltd — Battery Hall SIS\nIEC 61511 SIL 2 Certification — Certified 18 months ago\n\nCERTIFIED OPERATING SETPOINTS (MANDATORY — DO NOT MODIFY WITHOUT RECERTIFICATION):\n THERMAL_RUNAWAY_THRESHOLD: 55°C\n H2_ALARM_THRESHOLD: 1.0% LEL\n MAX_CHARGE_VOLTAGE: 4.25 V/cell\n\nPATCH STATUS:\n SIS Firmware 2.4.1 — available from vendor for 18 months.\n Status: DEFERRED pending recertification budget approval.\n Recertification cost estimate: £180,000 | Duration: 8 weeks offline.\n Risk assessment status: Accepted — 'Low probability; maintain compensating controls.'\n\nANY MODIFICATION to SIS setpoints requires SIL 2 recertification under IEC 61511.\nA modified but uncertified SIS is not a SIS — it is an unvalidated control system." "puzzle_graph_role": "clue", "puzzle_graph_aim": "investigate_sis", "puzzle_graph_reveals": "Certified THERMAL_RUNAWAY_THRESHOLD: 55°C — confirms SIS tamper; SIS firmware patch deferred 18 months; £180k recertification cost cited as barrier", "text": "SAFETY REQUIREMENTS SPECIFICATION\nAlbion Energy Storage Ltd — Battery Hall SIS\nIEC 61511 SIL 2 Certification — Certified 18 months ago\n\nCERTIFIED OPERATING SETPOINTS (MANDATORY — DO NOT MODIFY WITHOUT RECERTIFICATION):\n THERMAL_RUNAWAY_THRESHOLD: 55°C\n H2_ALARM_THRESHOLD: 1.0% LEL\n MAX_CHARGE_VOLTAGE: 4.25 V/cell\n\nPATCH STATUS:\n SIS Firmware 2.4.1 — available from vendor for 18 months.\n Status: DEFERRED pending recertification budget approval.\n Recertification cost estimate: £180,000 | Duration: 8 weeks offline.\n Risk assessment status: Accepted — 'Low probability; maintain compensating controls.'\n\nANY MODIFICATION to SIS setpoints requires SIL 2 recertification under IEC 61511.\nA modified but uncertified SIS is not a SIS — it is an unvalidated control system.", - "onRead": { "setVariable": { "sis_tamper_confirmed": true } } + "onRead": { "setVariable": { "sis_certification_seen": true } } }, { "type": "notes2",