Skip to content
MajorIncidentPublic

About

Browser-first Kepner–Tregoe incident workbook for rapid facilitation, AI-assisted analysis, resilient state, and optional real-time collaboration.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

KT Intake – AI-Optimized Incident Analysis Template

KT Intake is a browser-first Kepner–Tregoe (KT) incident workbook designed for rapid bridge facilitation, AI-assisted summaries, resilient state restoration, and optional shared collaboration. The primary UI is delivered from index.html and ES modules; collaboration is backed by Vercel Functions under api/ with Neon persistence, while normal local intake use remains browser-resident.

Quickstart

  • Clone or download this repository.
  • Open index.html in any modern browser. Standalone use remains local-first and does not depend on the classroom backend.
  • A genuinely new browser asks whether to Work independently, Join a class, or Teach a class. Existing saved Intakes and existing ?workspace= collaboration links migrate silently to Standalone so the new chooser does not interrupt established workflows.
  • The selected experience resumes from the separate kt-experience-role-v1 preference. Intake work itself still loads from kt-intake-full-v2, with action plans under kt-actions-by-analysis-v1.
  • Use the header controls to Save to File (exports a JSON snapshot) or Load from File (imports a previously saved snapshot) when you need to move an intake between browsers or machines.
  • Open the shared resource drawer to work with curated material. Standalone receives public Standard Templates only. Connected Students receive Standard Templates plus Classroom-authorized Case Studies; connected Instructors receive authorized teaching Case Studies. The rotating Case Study mode password remains a learning/progression control, not authentication.

Development Setup

AI contributors should run the following commands (or manual preview) whenever the described workstream applies so linting, templates, and docs stay current.

Command When to run it Notes & references
npm ci / npm install Run once after cloning or whenever package.json changes. Installs the pinned toolchain for scripts, tests, and template validation. Node 24 is the repository baseline; .nvmrc is authoritative. See docs/AI-ONBOARDING.md.
npm run dev During day-to-day feature work that touches src/, components/, or scripts/. Starts the watcher so template manifests regenerate automatically; pair it with the guidance in docs/commenting-guide.md when wiring new anchors.
Open index.html directly For quick manual QA or smoke tests that do not require the watcher. The static file reflects the latest bundle after any build step, so you can double-check flows without Node running.
npm run build After editing authored JSON under templates/ and before opening a pull request. Validates all authored resources and regenerates both the public Standard Template manifest and server-only protected Case Study manifest. This is a repository-authoring command, not the Vercel production command.
npm run build:templates Immediately after editing JSON under templates/ or generated-manifest logic. Validates every authored resource, writes public Standards to src/templates.manifest.js, and writes protected Case Studies to api/protected-case-studies.manifest.js.
npm run verify:tests Any time you change runtime code under src/, components/, or server api/. Enforces the coverage contract described in docs/testing-guidelines.md and scaffolds missing suites.
npm run verify:summary Whenever you add or change form controls/options. Ensures new inputs are wired into the Copy & Paste Summary, documented, and styled with the Apple-like rhythm. See docs/summary-style-checklist.md.
npm run verify:persistence When adding or editing inputs/captions that should survive reloads. Confirms new controls tie into src/appState.js and src/storage.js, prompting template/state updates so saves/loads remain lossless.
npm test Before committing or when adding new suites. Runs the full test matrix (DOM + unit) so CI sees the same state you validated locally.
npm run verify:protected-cases After any template/classroom/deployment boundary change. Proves protected Case Study IDs/names are absent from public browser runtime assets and that authored templates/*.json remains excluded from Vercel deployment.
npm run verify:vercel-functions After adding or moving anything under api/, or changing Vercel routing. Conservatively counts deployable api/*.js entrypoints and fails above the Hobby-plan budget of 12. Classroom public URLs are intentionally multiplexed through one function.
npm run quality Before marking any pull request ready. Canonical repository gate: lockfile, repo doctor, domain guards, lint, generated-file freshness, protected-case boundary, storage docs, and the full test suite. See docs/REPOSITORY-OPERATIONS.md.
npm run update:storage-docs / npm run check:storage-docs Run update whenever you alter persisted schema, then check before pushing. Keeps docs/storage-schema.md and docs/storage-schema.appendix.md synced with new keys or shapes.

Entry Point & Boot Logic

  • index.html declares the full UI layout and loads the JavaScript bundle via <script type="module" src="main.js"></script>.
  • main.js waits for DOMContentLoaded, then calls boot(). This bootstraps every feature in order:
    1. Configure the KT table utilities (configureKT) with callbacks such as autoResize, updatePrefaceTitles, and showToast.
    2. Initialise the preface, communications log, KT table, steps drawer, and possible-causes UI.
    3. Wire the summary buttons and communication controls, plus Alt-key shortcuts for power users.
    4. Restore any previous session from localStorage via restoreFromStorage() → applyAppState().
    5. Expose temporary global fallbacks (window.onGenerateSummary, etc.) so legacy bookmarks continue to work while modules take over.

Module Architecture

See docs/architecture-overview.md for the boot sequence narrative, module ownership map, and detailed cross-module data-flow reference. The quick table below summarizes the primary runtime files.

File Purpose
src/constants.js Deep-frozen config: KT table rows, phase metadata, finding modes, and step definitions.
src/storage.js Helpers that persist and hydrate the entire UI state under kt-intake-full-v2.
src/majorIncidentAnalysis.js Owns the Major Incident-only Decision Analysis and action-compatible Potential Problem Analysis cards after Possible Causes.
src/appState.js Collects and reapplies UI state across modules (collectAppState, applyAppState, getSummaryState).
src/notesWorkspace.js Owns the persistent Notes workspace, accessible note placement, drag/drop validation, and its snapshot state.
src/preface.js Manages bridge activation fields, mirror sync, detection chips, and token updates for {OBJECT} / {DEVIATION}.
src/kt.js Builds the KT IS/IS NOT table, manages paired facts, possible causes, and related UI affordances.
src/steps.js Controls the incident checklist drawer, keyboard shortcuts, and completion metrics.
src/comms.js Handles comms logging, cadence timers, and restoring the communications pane.
src/summary.js Generates formatted summaries and AI prompts; exposes generateSummary() and state providers.
src/toast.js Minimal toast notification system used by comms and global alerts.
src/fileTransfer.js Bridges collectAppState() / applyAppState() with Blob/FileReader APIs for Save/Load workflows.
components/actions/ActionListCard.js Renders the action list card UI, wires inline editing, and notifies listeners when actions change.
src/actionsStore.js Persists actions by analysis ID under kt-actions-by-analysis-v1, providing CRUD and sorting helpers for the card UI.
src/intakeTargets.js Universal semantic Intake-target registry for static/KT targets, serialized snapshot projection, live placement resolution, and deterministic versioned fingerprints. Target IDs are domain identity; Templates and DOM placement are not.
src/coachableFields.js Backward-compatible coaching facade over src/intakeTargets.js; preserves existing coaching export names and stored target IDs without owning a second registry.
src/classroomCoaching.js Instructor coaching controls and Student read-only feedback UI backed by the separate Classroom coaching API.
src/classroomCaseStudies.js In-memory authorized Classroom Case Study catalog/payload client. It receives active Student/Instructor capabilities from their lifecycle controllers and never persists them.
main.js Entry point that imports every module, wires shared events, and runs boot().

Storage keys

  • kt-intake-full-v2: Primary snapshot containing the intake form, table, steps, communications log, possible causes, and notesWorkspace notes/open preference. Save to File and Load from File include this full snapshot automatically.
  • kt-experience-role-v1: Local-only Standalone / Student / Instructor preference. It is deliberately excluded from collectAppState(), Intake file exports, summaries, templates, and Start Fresh clearing.
  • kt-classroom-student-session-v1: Student same-device resume key. Its current v2 envelope contains the stable Student class-session capability plus public class/participant/current-assignment context; the assignment-specific workspace capability remains memory-only and is reacquired after reload or reassignment. Older envelope formats are intentionally unsupported before production. The envelope never enters Intake exports/summaries/templates.
  • kt-classroom-student-local-recovery-v1: Local recovery snapshot captured immediately before joining a class so Leave class can restore the prior local Intake. It is separate from the active Intake snapshot and classroom credentials.
  • kt-classroom-instructor-session-v1: Local-only Instructor same-device resume envelope containing the Instructor class capability, public class metadata, and the last selected public workspace ID. It is never collected into Intake state, files, summaries, templates, or Student workspace credentials.

Coaching feedback is server-side Classroom data, not a local Intake storage key. It lives in classroom_coaching_feedback and is deliberately excluded from kt-intake-full-v2, Save/Load, templates, summaries, and collaboration snapshot revisions.

Experience roles

Experience role is a product-level choice, not an Intake workflow mode. General / IT / Pharma / Major Incident remain controlled by meta.intakeMode; Standalone / Student / Instructor are controlled separately by src/experienceRoles.js and src/experienceRoleController.js.

  • Standalone exposes the normal Intake and current collaboration behavior. Its resource drawer contains Templates only.
  • Student joins with a display name and one human class code. An Instructor share/QR link may prefill that same code through a client-only #join= fragment; the fragment is consumed locally and never replaces normal server admission. Admission creates a stable high-entropy Student class-session capability; the learner may remain Waiting / unassigned with no workspace edit authority until the Instructor assigns a team or individual workspace. Once assigned, the browser exchanges the class session for a fresh assignment-specific editable workspace capability, keeps that workspace capability memory-only, and attaches it to the existing collaboration engine without entering the URL. Reassignment disconnects old authority before the Student enters the destination team's existing Intake; unassign returns the Student to Waiting. Connected Students see class/workspace/identity context, public Templates, protected Classroom Case Studies, and read-only Instructor coaching including optional notes and Changed since review. On narrow screens the Class, Case, Team, and Notes secondary surfaces default compact but remain one-action accessible; those collapse states are presentation-only. Switching away pauses live classroom sync while preserving class resume; Leave class clears resume and restores the local Intake captured before joining.
  • Instructor uses Start a class, receives one human Student join code, and can copy the code, share a fragment-only join link, or show a fully local QR for that same safe link. The Instructor sees Waiting/assigned participants, creates team or individual workspaces, and assigns/reassigns/unassigns Students through accessible selectors. The Instructor capability is retained locally for same-device resume; lost cross-device authority will be handled by the separately authorized Administration / Maintenance experience in #329 rather than a public bearer-code form. The dashboard can rapidly switch into a live read-only view of each Student/team Intake and provide field-level coaching. Observation uses the normal Intake renderer but server authorization keeps the Instructor credential outside Student edit capability. The same Instructor class capability authorizes protected teaching Case Studies. The Class rail defaults compact on narrow screens without changing session authority. Switching away pauses observation while preserving same-device class resume; Leave class clears the Instructor resume and restores the instructor's prior local Intake.
  • Use View → Experience to switch roles without changing or deleting Intake data.

Notes workspace

Use View → Notes workspace or Alt+N to open or collapse the persistent notes dock. On narrow screens Notes defaults compact; opening/collapsing it there is presentation-only and does not rewrite the persisted desktop notesWorkspace.open preference. Add short capture notes, then quickly edit or delete them before placing them into the intake. Drag a note to an editable text field in the main intake form. Keyboard users can focus a field, then focus and activate the note card with Enter or Space. Successful placement removes the note and saves the intake; invalid targets retain it.

  • kt-actions-by-analysis-v1: Dedicated action registry keyed by analysis ID that powers the action list card and owner audit trail.

Shared collaboration

The Collaboration menu is available in every intake mode. Starting a session opens a short identity form where the creator can set an optional team name and personal display name. A blank team name becomes Shared intake; a blank personal name receives a stable, friendly Teammate N label. Opening a link containing ?workspace=<secret-token> loads that workspace through the server API, applies the snapshot with applyAppState(), then asks the visitor how they would like to appear before registering them as present. The Team workspace bubble sits immediately above the Notes workspace and shows the team, every current participant, the local person marked with “(you),” presence freshness, and actions for editing personal and team names. On screens up to 700px the Team card defaults compact and expands locally without changing token, revision, presence, team metadata, or snapshot state. Participant chips wrap onto additional rows so the workspace grows to fit the active room instead of hiding people in a horizontal scroller.

Display names are stored as a device preference under kt-collaboration-profile-v1 and can be changed from the workspace strip or Collaboration menu. Classroom Student sessions reuse this presence identity, but they attach their server-issued workspace capability programmatically rather than through ?workspace=. Direct collaboration-link copying, legacy shared-session leaving, and team renaming are disabled in the classroom Student UI; the Student experience owns class exit/resume semantics. This profile is deliberately excluded from collectAppState(), exported intake files, summaries, and kt-intake-full-v2. Names are visible to anyone with the secret link. Any active collaborator can also edit the shared team name because secret-link workspaces grant equal edit capability and do not have administrator roles. Team-name changes are workspace metadata and do not advance the intake snapshot revision.

While a shared session is active, the browser-tab title combines the current problem statement with up to four active participant names and a remaining-member count. This makes the relevant incident and room visible when moving between tabs. Leaving the shared session restores the normal intake title.

Secret-link security model

Possession of the full link grants read and edit access in v1. Share it only with incident participants and avoid pasting it into tickets, chat rooms, analytics, or logs. The browser calls the stable /api/workspaces/session endpoint and sends the capability in an Authorization: Bearer header, never in an API URL. Neon stores only its SHA-256 hash, never the raw token, and responses carry Cache-Control: no-store and Referrer-Policy: no-referrer. There is deliberately no workspace-list endpoint. Leaving removes the token from the current URL; it does not revoke the link or delete shared data.

Neon and Vercel configuration

The existing Vercel project is intake, with the neon-intake integration expected to inject a server-side connection string. The API detects DATABASE_URL first, then the common integration aliases POSTGRES_URL and NEON_DATABASE_URL. Set one of those variables for Preview and Production environments; never expose it with a VITE_, NEXT_PUBLIC_, or other browser-visible prefix. WORKSPACE_EXPIRY_DAYS is optional and defaults to 30 when missing or invalid.

@neondatabase/serverless is imported lazily by the Vercel Function, so static builds and browser modules never need database credentials. On the first database request, the server idempotently creates or extends collaboration_workspaces and creates or extends collaboration_participants plus their indexes. The workspace table contains a generated ID, token hash, JSONB snapshot, positive integer revision, team name, next friendly participant number, created/updated timestamps, and expiry timestamp. Participant rows contain the workspace ID, opaque browser participant UUID, display name, stable fallback number, join time, last-seen time, and ephemeral editing field/revision. This automatic initialization means normal deployments do not require a person to edit the production database. A human must still confirm that the Neon integration exposes one supported connection variable to each desired Vercel environment and that the database role can create and alter the tables/indexes on first use.

Classroom class API

Classroom slice #291 layers class organization and authorization over the existing collaboration engine; it does not create a second Intake synchronization system.

The core class routes are:

  • /api/classes — create/administer one class with an Instructor bearer capability and return the normal human Student join code;
  • /api/classes/workspaces — Instructor-only creation/listing of individual/group workspaces;
  • /api/classes/participants — Instructor-only live roster and assign/reassign/unassign transitions;
  • /api/classes/admit — one-code Student admission into a class-session / Waiting state;
  • /api/classes/student — represented Student's own assignment status only;
  • /api/classes/student/access — fresh assignment-specific editable workspace capability while currently assigned;
  • /api/classes/observe — Instructor-only read-only live workspace observation;
  • /api/classes/coaching and /api/classes/coaching/student — separate Instructor-write / Student-read coaching channel;
  • /api/classes/case-studies and /api/classes/case-studies/student — protected Case Study catalog and POST payload delivery after Classroom authorization.

The human class code is admission-only. A successful admission returns a high-entropy Student class-session capability that can read only that learner's assignment state. When assigned, the Student exchanges it for a fresh classroom-student workspace capability accepted by the existing /api/workspaces/session and /api/workspaces/presence endpoints. Old workspace authority is revoked on reassignment/unassign.

The database stores only SHA-256 bearer-capability hashes. CLASS_EXPIRY_DAYS optionally controls class retention and defaults to 30 days. Classroom collaboration workspaces inherit the class's exact absolute expiry, so they cannot outlive it. Instructor authority can be rotated; reassignment/unassign revokes the represented Student's prior workspace capability, and class revocation invalidates class-owned access server-side.

See docs/classroom-api.md for the endpoint/capability matrix and SECURITY.md for the security boundary. Instructor observation is implemented through the separate GET-only /api/classes/observe path, authorized by the Instructor class capability after class/workspace ownership is proven. It never resolves through the editable workspace-alias path.

The public /api/classes/** URL contract is preserved while Vercel packaging is consolidated: vercel.json rewrites those paths into the single api/classroom.js Serverless Function, which delegates through api/_classroomRouter.js to the same class/coaching/protected-case/exercise handlers. The rewrite marker is routing only and grants no authority. Do not restore one Vercel function file per Classroom endpoint; npm run verify:vercel-functions protects the deployment budget.

#313 staged simulation is implemented end-to-end and production-published. Protected Case Studies may carry an explicit server-only simulation definition; additive Neon tables persist class exercise lifecycle, optional releases, workspace readiness, and immutable debrief checkpoints. Running exercises pin the staged-definition version plus a definition fingerprint and inherit the owning class expiry. Instructor APIs/UI support staged selection, Start/Pause/Resume, controlled evidence release, debrief, explicit edit freeze/unfreeze, checkpoint inspection, Advance, and Complete. Student staged reads use the stable class-session capability, expose only current/cumulative Student-safe material, support Ready/Resume Working, and project frozen debrief Intake read-only while the server enforces writes with HTTP 423. These tables and clients do not duplicate live Intake state: collaboration workspaces remain the only live snapshot/revision source of truth, checkpoint snapshots are historical read-only evidence, and Classroom exercise/session state stays outside Intake persistence/export/summary. Production official Case Study stage content remains intentionally unauthored until authoritative material is supplied.

Conflict recovery and two-window testing

Updates use optimistic revision control: each complete snapshot retains the revision observed when it was captured. Text edits wait about 300 ms so typing is responsive, blur flushes pending text, and completed select, checkbox, radio, and button-driven changes save immediately where the owning control is identifiable. Only one PUT is sent at a time; edits made during that request collapse to the latest snapshot and follow the successful response at its returned revision. A stale write receives HTTP 409 and cannot overwrite the newer row.

While the page is visible and online, the client checks approximately every 900 ms with afterRevision; an unchanged revision produces an empty 204 response. GETs never overlap, and stale session responses are ignored. Network and 5xx failures use exponential backoff (up to 30 seconds) and return to the normal cadence after success. Mobile browsers may suspend timers, so visibility restoration, window focus, pageshow (including back-forward-cache restoration), and returning online trigger an immediate revision check and safe pending-save flush. Sync now performs the same safe flush and check without bypassing revision protection.

Presence is deliberately independent of snapshot revisions: visible, online clients heartbeat /api/workspaces/presence about every two seconds while leading-edge focus and typing transitions are sent immediately. Rich, stable-color participant chips distinguish active, viewing, editing, and recently idle teammates; they show the accessible label of a teammate's current or last-known field and collapse behind a keyboard-accessible +N more control in larger rooms. Typing settles to viewing after roughly 1.5 seconds without input, then to idle after 30 seconds, while recently idle people remain visible for up to five minutes. Animated chips and field badges are restrained and honor prefers-reduced-motion.

While someone types in an identified form control, that field receives a softly color-coded outline and an animated “Name is editing…” badge in everyone else's browser. Focused-but-not-typing teammates appear as “Name is here.” Entering an occupied field produces a one-time screen-reader-friendly advisory that simultaneous saves may require conflict review, but collaboration is intentionally non-blocking: fields are never disabled or locked. Activity contains only the control ID, state, sequence, and current revision—never draft text. A latest-state-wins presence queue coalesces rapid transitions, follows an in-flight request with the newest state, and ignores older server activity sequences. Heartbeats, joining, leaving, renaming, and activity updates never increment the intake revision or create snapshot conflicts. Hidden tabs suspend regular heartbeats; focus, visibility restoration, pageshow, reconnecting, and Sync now refresh them. Explicitly leaving removes the participant immediately when possible, while crashes and closed tabs age out automatically. During offline periods, the last participant list stays visible with a warning that presence may be out of date.

The Collaboration menu reports Shared · Revision …, last successful sync age, pending saves, offline recovery, retries, and conflicts. If polling finds a newer revision while local work is queued or in flight, the browser does not apply it silently: it stores the local version under kt-collaboration-recovery-v1, shows Conflict · Review required, and offers Load newest shared version or Export local recovery. Loading shared state does not delete that recovery record; neither does leaving the session. Invalid (400/401) and missing or expired (404) sessions stop polling.

To preview manually:

  1. Deploy or run an environment with a Neon connection variable and open the intake in window A.
  2. Enter a recognizable value, choose Collaboration → Start shared session, then copy the collaboration link.
  3. Open that link in window B, choose a display name, and confirm the value loads, both indicators show Synced, and both names appear beneath the header.
  4. Edit in one window, stop typing, and confirm the other updates after the debounce plus a polling interval.
  5. To exercise a conflict, make edits in both windows before either receives the other's revision. Confirm one browser shows Conflict, then export its recovery before choosing whether to load the newest shared copy.
  6. Disconnect the network briefly to confirm Offline, reconnect, and verify polling resumes. Leave the session and confirm the local intake remains available.

This is snapshot-based collaboration, not character-level co-editing. Concurrent edits to different fields can still conflict; there is no merge UI, verified identity, audit trail, workspace role model, link revocation, end-to-end encryption, or server-side deletion action. Presence means only that a browser with the secret link has recently sent a heartbeat; it does not prove authorship or identity. Expired rows behave as missing (404); physical cleanup of expired rows can be added later without changing that behavior.

Manual collaboration checks

  1. Open one shared link in two browsers, edit a text field in browser A, and confirm browser B receives it after the debounce and poll interval.
  2. Edit both browsers before either synchronizes and confirm the stale writer shows conflict recovery rather than overwriting.
  3. On a phone, background the page, edit the workspace from the second browser, then return through the app switcher and browser Back/Forward navigation; confirm synchronization resumes without refresh.
  4. Disable networking, make an edit, confirm Offline · Changes kept locally, restore networking, and confirm the queued edit either saves at its expected revision or enters conflict.
  5. Select Sync now with and without a pending edit and confirm requests remain serialized and the displayed revision advances.
  6. Join with a blank name and confirm a stable Teammate N label appears in both windows, then rename it and confirm no snapshot revision is created.
  7. Stop typing and confirm the remote chip changes from Editing to Viewing after roughly 1.5 seconds, then Idle after 30 seconds; close a participating tab without leaving and confirm it ages out after the five-minute recent-presence window.
  8. Rename the team and confirm all participant chips remain visible, the browser-tab title includes the problem statement and members, and the intake revision does not change.
  9. Focus a field another person is editing and confirm the advisory is announced once while the field remains fully editable.
  10. Enable reduced motion and confirm state, location, and color remain clear without pulsing or sliding animation.
  • kt-collaboration-recovery-v1: A conflict-only local recovery envelope containing the losing snapshot and capture time; it is intentionally separate from the normal intake key.
  • kt-collaboration-profile-v1: A local-only participant UUID and last display name used to prefill future shared-session joins; it is never included in intake snapshots or file exports.

Need to know which module owns a given storage field? Jump to the Storage-to-Module Responsibility Map for a field-by-field lookup tied to the DOM anchors and runtime files that persist each value.

Development Guidelines

  • Change only the module that owns the UI slice you are updating; avoid cross-module DOM mutations.
  • Use src/constants.js for shared enums or immutable data instead of duplicating literals.
  • When wiring new behaviour, export it from a module in src/ and import it in main.js. main.js should stay focused on orchestration.
  • Preserve anchor comments in index.html (e.g., [styles], [section:preface]) so automation and documentation links remain stable.
  • Keep the UI accessible: reuse layout classes, maintain contrast, and follow the Apple-like spacing guidance in AGENTS.md.

Template manifest workflow

  • Curated resources live as authored JSON snapshots under templates/ (one file per resource). Each file lists metadata (id, name, description, templateKind, supportedModes) plus a SerializedAppState payload.
  • npm run build:templates validates all authored resources, including universal Intake-target coverage for public Standard Templates and shared-namespace validation for staged intakeTargetIds, then generates two explicit boundaries:
    • src/templates.manifest.js — public Standard Templates only;
    • api/protected-case-studies.manifest.js — server-only Case Study metadata and full payloads.
  • templateKind: standard means a reusable public Template. Standard Template reasoning fields use the same target registry consumed by coaching/#319; adding a new Template that uses existing fields requires no template-specific mapping. templateKind: case-study means a protected Classroom Case Study. Staged intakeTargetIds may reference registered static/KT target IDs or family IDs such as possible-cause, but never learner-created dynamic instance IDs. src/templateAvailability.js still owns role semantics; src/classroomCaseStudies.js supplies only the currently authorized protected catalog/payloads.
  • Authored templates/*.json files are build-time source and are excluded from Vercel deployment by .vercelignore. Never import the server-only protected manifest from browser modules.
  • Run npm run verify:protected-cases after changing this boundary and npm run quality before handoff. The rotating Case Study mode password remains pedagogy only; Classroom capability authorization is the confidentiality boundary.
  • npm run dev and local npm run build still regenerate both manifests for maintainers. Commit the authored JSON and both generated manifests together.

Vercel deployment

  • Production Vercel builds execute npm run verify:vercel-functions && npm run verify:protected-cases && npm run build:vercel-public (see vercel.json). They do not regenerate manifests, because authored templates/*.json source files are deliberately excluded from the deployment upload.
  • scripts/build-vercel-public.mjs creates the only public static surface under dist/: index.html, main.js, styles.css, browser JavaScript under src/ and components/, plus the explicitly public docs/eula.md linked from the footer. All other docs, tests, build scripts, authoring JSON, AGENTS files, and repository metadata are not copied.
  • vercel.json must keep outputDirectory: "dist". The former repository-root output exposed internal files such as /docs/classroom-workstream.md and /scripts/build-templates-manifest.mjs.
  • Generated manifests are committed artifacts. GitHub CI remains responsible for running npm run build:templates / check:templates and proving they match authored JSON; npm run quality also builds/verifies the same minimal dist/ bundle.
  • .vercelignore prevents raw authored Case Study JSON from entering the Vercel source upload, while the dist/ build prevents all non-runtime repository files from becoming public static assets.
  • Vercel Git deployments are deny-by-default for ordinary branches. main deploys automatically; short-lived verify/** branches are the explicit preview path for security/E2E checks. vercel.json also owns the ignored-build decision so those two allowed branch classes proceed even if the project dashboard has an older Ignored Build Step configured. This prevents AI checkpoint commits from exhausting Vercel build capacity while preserving GitHub CI on every PR commit.
  • The linked Vercel project currently runs on the Hobby plan, which permits at most 12 Serverless Functions per deployment. Classroom routes therefore share one deployment entrypoint. Server helpers stay in underscore-prefixed api/_*.js modules, and collaboration workspace endpoints remain separate.
  • When modifying the manifest workflow, public bundle, or required quality gates, update both this README and vercel.json so the documented steps mirror the actual hosted build.
  • Dependency changes must be installed through the normal npm registry so npm generates the complete lockfile. Run npm run verify:lockfile before committing and npm ci from a clean dependency tree; the offline guard catches missing resolved root-package entries before CI reaches its clean install.

Documentation & anchor hygiene

  • Add or update module docblocks and JSDoc summaries whenever you touch a runtime file. The patterns in docs/commenting-guide.md are canonical.
  • Register every new anchor in the commenting guide and mirror the change in scoped AGENTS.md files so AI agents can locate feature boundaries quickly.
  • Before merging a feature, confirm that affected README sections and any relevant AGENTS.md anchors reflect the change set. Treat doc refreshes as part of the feature, not a follow-up task.

AI & Automation Notes

  • State helpers provide a stable integration surface:
    import { collectAppState, applyAppState, getSummaryState } from './src/appState.js';
    import { generateSummary } from './src/summary.js';
    
    const snapshot = collectAppState();
    // ... mutate the DOM, then roll everything back
    applyAppState(snapshot);
    
    // Produce a formatted narrative or AI prompt
    generateSummary('summary', 'prompt preamble');
  • collectAppState() serialises the entire UI and should be called before tests mutate the DOM.
  • applyAppState() rehydrates the UI, letting Playwright/Cypress tests verify round trips without manual input.
  • getSummaryState() is injected into the summary module so assertions can compare the most recent export.
  • The KT table exposes configureKT() to register callbacks. Pass only the dependencies your module needs; avoid hidden globals.

Testing & QA

CI status CodeQL security scan status

  • Automated coverage is mandatory: Every feature pull request must add or update tests that assert the behaviours introduced or modified.
  • Required real-browser regression: The branch-protected tests CI status runs npm run quality and then the Playwright/axe suite via npm run test:browser. Browser failures therefore block the existing required check. The deterministic browser server does not depend on Vercel previews or production credentials; the separate Browser E2E workflow is manual-only for focused reruns.
  • Security scanning is automatic: GitHub CodeQL runs on every push to main, pull request, and a weekly schedule to flag JavaScript/TypeScript issues without manual setup.
  • Dependency review gate: A GitHub dependency review workflow now blocks merges when a pull request introduces new high or critical advisories, so expect PRs to fail even if unit tests succeed until vulnerable dependencies are replaced or patched.
  • Run the coverage guard: Execute npm run verify:tests after staging runtime changes. The guard ensures any updates under src/ or components/ are paired with refreshed suites in tests/**/*.test.mjs.
    • The command runs scripts/ensure-tests-cover-changes.js, which inspects your git diff (against origin/$GITHUB_BASE_REF or main) to see whether runtime files changed without touching tests/*.test.mjs.
    • When a gap exists, the guard copies tests/template.feature.test.mjs into tests/auto-generated/<feature>.feature.test.mjs, adds a banner describing which file triggered the stub, and exits non-zero so you fill in the test before continuing. Delete the generated stub once a real test lives in the proper suite.
    • CI executes the same guard (npm run verify:tests) ahead of the test run, so pull requests fail fast if runtime diffs land without coverage. Fix failures by running the guard locally, replacing each skipped placeholder with real assertions, and re-running the command until it exits cleanly.
  • Keep new UI controls summary-ready: Run npm run verify:summary whenever you add inputs or dropdown options. The guard rejects changes that add form controls without also updating summary wiring, summary-focused tests, or the styling/documentation notes in docs/summary-style-checklist.md.
  • Persist new inputs and captions: Run npm run verify:persistence when introducing form fields or caption inputs. The guard scans diffs for new controls under index.html, components/, or src/ and fails if src/appState.js / src/storage.js (or relevant templates) stay untouched, providing remediation steps to keep save/load flows lossless.
  • Choose the right suite: Follow docs/testing-guidelines.md to decide between unit and DOM integration tests, apply the naming/location conventions under tests/, and leverage the reusable template in tests/template.feature.test.mjs when starting new files.
  • Auto-generated stubs live in tests/auto-generated/: When the guard detects missing coverage it copies tests/template.feature.test.mjs into a feature-specific stub so you can immediately replace the skipped test with meaningful assertions. Commit the file once it contains real coverage or delete it if you move the tests elsewhere.
  • Custom Node test loader: npm test invokes node --test --loader ./tests/test-loader.mjs ... so suites can opt into module stubs and jsdom globals. Review the loader, TEST_STUB_MODULES, and tests/helpers/jsdom-globals.js workflow in docs/testing-guidelines.md.
  • Snapshot-friendly helpers: Automated harnesses should call collectAppState() / applyAppState() for reliable state restoration and generateSummary() for output verification.
  • Manual regression: Open index.html, fill representative data, click Generate Summary, then refresh to ensure state persistence.
  • Storage changes: Run npm run update:storage-docs after altering persisted fields. CI can enforce freshness with npm run check:storage-docs.

Continuous integration runs automatically on pull requests and pushes to main, using the repository's Node.js version via actions/setup-node with npm caching. The required tests job executes npm ci, the canonical npm run quality gate, installs Chromium, and then runs npm run test:browser. Node/browser failure artifacts are retained only on failure; the focused Browser E2E workflow remains available through manual dispatch.

Additional Documentation

  • See AGENTS.md for global UI principles, module isolation rules, and contribution contracts.
  • Refer to index.AGENTS.md when altering the HTML structure; it documents anchor expectations and storage invariants.
  • For AI-specific onboarding notes, including module extension patterns, read docs/AI-ONBOARDING.md.
  • Consult docs/commenting-guide.md for required docblocks, anchor formats, and the merge checklist.

Major Incident role guidance

Selecting Major Incident Management reveals the [feature:major-incident-role-legend] four-phase role guide and contextual ownership badges beside Problem Summary, Impact, Problem Analysis, Possible Causes, Decision Analysis, Potential Problem Analysis, Communications, Steps, and every mapped KT question. Each badge pairs a consistent outline icon with visible phase-and-lead text, while its accessible description also names the primary, consulted, and approval roles. Blue, red, green, and orange accents identify the four phases with high-contrast text, leading borders, and keyboard focus indicators, so colour is never the only cue. In this mode, the KT table uses the semantic red Problem Analysis treatment instead of its non-semantic blue and lavender row accents. Switching modes hides this presentational guidance without unmounting controls or deleting entered data.

Decision and Risk gate entries remain portable in the main kt-intake-full-v2 snapshot: decisionAnalysis records the decision, options, owner, rationale, and timestamp, while potentialProblemAnalysis records the action-compatible owner, risk, change-control/rollback, and verification fields. A collectAppState() snapshot can therefore be reapplied with applyAppState() after a mode change without losing either gate.

About

Browser-first Kepner–Tregoe incident workbook for rapid facilitation, AI-assisted analysis, resilient state, and optional real-time collaboration.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages