Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
0c6bf97
docs: define live classroom management contract
MajorIncident Oct 6, 2026
99c31f2
feat: add live classroom participant persistence
MajorIncident Oct 6, 2026
fa6180a
fix: preserve participant class on workspace deletion
MajorIncident Oct 6, 2026
5665637
test: model live classroom participants in memory
MajorIncident Oct 6, 2026
df3633d
test: cover live join code and waiting roster primitives
MajorIncident Oct 6, 2026
104a736
docs: checkpoint live classroom persistence tranche
MajorIncident Oct 6, 2026
8eed407
docs: advance live classroom cold-start checkpoint
MajorIncident Oct 6, 2026
746559e
feat: add live class access primitives
MajorIncident Oct 6, 2026
1b81d4a
test: support live student access in repository harness
MajorIncident Oct 6, 2026
2d6f92d
feat: add live classroom HTTP handlers
MajorIncident Oct 6, 2026
11a83da
feat: expose live classroom API routes
MajorIncident Oct 6, 2026
50dd07a
feat: expose live classroom API routes
MajorIncident Oct 6, 2026
4d1910d
feat: expose live classroom API routes
MajorIncident Oct 6, 2026
dc698c1
feat: expose live classroom API routes
MajorIncident Oct 6, 2026
04f95f2
test: cover live classroom HTTP authorization
MajorIncident Oct 6, 2026
86632f2
docs: checkpoint live classroom HTTP tranche
MajorIncident Oct 6, 2026
6e1a023
docs: advance live classroom assignment checkpoint
MajorIncident Oct 6, 2026
742240e
feat: add server-side workspace presence cleanup
MajorIncident Oct 6, 2026
2c1d6fd
test: support assignment presence cleanup
MajorIncident Oct 6, 2026
b48e6bb
feat: add safe live participant assignment
MajorIncident Oct 6, 2026
a382aca
test: model live assignment transitions
MajorIncident Oct 6, 2026
7bfd662
test: prove live assignment authority cutoff
MajorIncident Oct 6, 2026
24487a6
docs: record live assignment API semantics
MajorIncident Oct 6, 2026
8d846ca
docs: harden live assignment server rules
MajorIncident Oct 6, 2026
87105fa
docs: checkpoint live assignment authority
MajorIncident Oct 6, 2026
27e9df7
docs: advance live classroom to Instructor UI
MajorIncident Oct 6, 2026
0d00455
feat: add Instructor live-management shell
MajorIncident Oct 6, 2026
9c26bdf
style: lay out live Instructor management controls
MajorIncident Oct 6, 2026
f230602
feat: add Instructor live-session client primitives
MajorIncident Oct 6, 2026
9e04de6
feat: render live Instructor class entry and join code
MajorIncident Oct 6, 2026
700163a
feat: render live Student roster in Instructor client
MajorIncident Oct 6, 2026
45314a5
feat: load live participants with Instructor class
MajorIncident Oct 6, 2026
c4db663
feat: add Start Class and live Instructor actions
MajorIncident Oct 6, 2026
a2ae981
feat: wire Instructor live-management interactions
MajorIncident Oct 6, 2026
40f6ece
fix: count live Students in Instructor workspace roster
MajorIncident Oct 6, 2026
8fe9e44
test: count live participants in workspace roster
MajorIncident Oct 6, 2026
09daa72
test: cover live Instructor class management
MajorIncident Oct 6, 2026
709cd91
test: cover Instructor join-code session helpers
MajorIncident Oct 6, 2026
2a76541
test: add live Instructor browser fixture state
MajorIncident Oct 6, 2026
e23b4cd
test: serve live Instructor management browser APIs
MajorIncident Oct 6, 2026
692c81a
test: cover live Instructor management in browser
MajorIncident Oct 6, 2026
6122ba2
chore: mark dynamic Instructor controls local-only
MajorIncident Oct 6, 2026
205b0ed
test: open existing Instructor class recovery path
MajorIncident Oct 6, 2026
05f2d0f
test: open existing Instructor class recovery path
MajorIncident Oct 6, 2026
d3e2a39
test: open existing Instructor class recovery path
MajorIncident Oct 6, 2026
1af1232
docs: checkpoint Instructor live management
MajorIncident Oct 6, 2026
d88a763
docs: advance live classroom to Student client
MajorIncident Oct 6, 2026
4748543
feat: make one-code Student join the primary path
MajorIncident Oct 6, 2026
73792b7
style: add Student waiting and legacy join states
MajorIncident Oct 6, 2026
d32c7ec
feat: add live Student waiting and reassignment client
MajorIncident Oct 6, 2026
db54b1a
test: cover live Student session isolation
MajorIncident Oct 6, 2026
a3d5206
test: cover live Student waiting and reassignment
MajorIncident Oct 6, 2026
8485b2c
test: add isolated live Student browser fixture
MajorIncident Oct 6, 2026
50f6260
test: cover one-code Student reassignment in browser
MajorIncident Oct 6, 2026
2fd0d53
test: keep waiting access fixture side-effect free
MajorIncident Oct 6, 2026
30dbb0d
test: open legacy Student two-code access
MajorIncident Oct 6, 2026
8ec6b28
test: open legacy Student two-code access
MajorIncident Oct 6, 2026
124d6d2
test: open legacy Student two-code access
MajorIncident Oct 6, 2026
1b90c0f
test: open legacy Student two-code access
MajorIncident Oct 6, 2026
0c8692c
fix: improve Student waiting contrast
MajorIncident Oct 6, 2026
080669d
docs: describe live Student client authority
MajorIncident Oct 6, 2026
551e966
docs: checkpoint live Student client
MajorIncident Oct 6, 2026
7387127
docs: advance classroom to integrated live acceptance
MajorIncident Oct 6, 2026
5f14049
test: share live Instructor workspace state
MajorIncident Oct 6, 2026
4f36af2
test: integrate live Student admission with Instructor class
MajorIncident Oct 6, 2026
e7b8d06
test: allow coaching on integrated live workspaces
MajorIncident Oct 6, 2026
2262490
test: add integrated live classroom browser acceptance
MajorIncident Oct 6, 2026
971898c
test: isolate integrated live classroom fixture
MajorIncident Oct 6, 2026
6c712e4
test: generalize managed live classroom fixture
MajorIncident Oct 6, 2026
9315210
test: route managed classroom fixtures by capability
MajorIncident Oct 6, 2026
11fb47d
docs: checkpoint integrated live classroom acceptance
MajorIncident Oct 6, 2026
01c20b7
docs: advance classroom to final readiness review
MajorIncident Oct 6, 2026
ac33d43
docs: reconcile Classroom API with live session model
MajorIncident Oct 6, 2026
fbcb20f
docs: make live classroom architecture canonical
MajorIncident Oct 6, 2026
da70d1e
docs: align API agent guidance with live classrooms
MajorIncident Oct 6, 2026
4b78757
docs: update README for live classroom lifecycle
MajorIncident Oct 6, 2026
fd59750
docs: update Classroom roadmap for completed live slice
MajorIncident Oct 6, 2026
eb22573
docs: secure live classroom resume authority
MajorIncident Oct 6, 2026
d6a2f27
docs: refresh Classroom AI onboarding map
MajorIncident Oct 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ See [`docs/architecture-overview.md`](docs/architecture-overview.md) for the boo

- `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 envelope containing class/workspace display metadata, participant display identity, and the issued per-participant workspace capability. It never contains the Student join or assignment code and is excluded from Intake exports/summaries/templates.
- `kt-classroom-student-session-v1`: Student same-device resume envelope. In the normal live-class path it 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. Legacy two-code sessions may still contain the issued workspace capability for compatibility. 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.

Expand All @@ -77,8 +77,8 @@ Coaching feedback is server-side Classroom data, not a local Intake storage key.
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** first joins an instructor-preconfigured class workspace using a class code, assignment code, and display name. The two admission codes are used once and discarded; the returned per-participant workspace capability is retained locally for same-device resume and is attached to the existing collaboration engine without entering the URL. Once connected, the Student sees class/workspace/identity context, public **Templates**, and a protected **Case Study** catalog fetched with that issued Classroom capability. Full Case Study state is fetched only on demand when the Student applies a case after the existing pedagogical mode/password step. Switching away from Student pauses the live classroom connection and clears the in-memory protected catalog while preserving class resume; **Leave class** clears resume and restores the local Intake captured before joining. Students also receive Instructor coaching beside the reviewed field, read-only, including optional notes and a **Changed since review** indicator when that field's current evidence no longer matches the reviewed fingerprint.
- **Instructor** opens an instructor-preconfigured class with the Instructor class capability, gets a searchable individual/team workspace rail, and can rapidly switch a **live read-only** view of each Student/team Intake. Observation uses the normal Intake renderer but the Student-owned controls are projected read-only; the Instructor credential is authorized only through the class-scoped GET observer path and never becomes a Student edit capability. The same Instructor class capability authorizes the protected teaching Case Study catalog/payload API. Switching away pauses observation and clears the in-memory protected catalog while preserving same-device class resume; **Leave class** clears the Instructor resume and restores the instructor's prior local Intake. In that read-only view, instructors can mark registered fields **Meets standard** or **Needs improvement**, add an optional note, revise/clear feedback, and see when the reviewed field has changed.
- **Student** normally joins with a display name and one human class code. 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. The older class-capability + assignment-capability flow remains under an explicit recovery disclosure. 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**. Switching away pauses live classroom sync while preserving class resume; **Leave class** clears resume and restores the local Intake captured before joining.
- **Instructor** normally uses **Start a class**, receives one human Student join code, 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; **Open an existing class** remains a recovery path. 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. 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
Expand Down Expand Up @@ -110,14 +110,18 @@ Classroom slice #291 layers class organization and authorization over the existi

The core class routes are:

- `/api/classes` — create/administer one class with an Instructor bearer capability;
- `/api/classes/workspaces` — Instructor-only creation/listing and assignment lifecycle for individual/group workspaces;
- `/api/classes/join` — Student admission into exactly one assigned workspace;
- `/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/join` — legacy two-code admission into exactly one assigned workspace;
- `/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.

A Student join requires **two independent capabilities**: the class Student-join capability and an assignment capability for one individual/group workspace. The Student join capability by itself cannot list assignments or classmates. A successful join returns a newly minted per-participant workspace capability that works with the existing `/api/workspaces/session` and `/api/workspaces/presence` endpoints.
In the normal live path, 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 older Student-join capability + assignment capability pair remains supported only as compatibility/recovery.

The database stores only SHA-256 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, Student-join, and assignment credentials can be rotated; class/assignment revocation invalidates access server-side.

Expand Down
10 changes: 5 additions & 5 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ Never commit, log, paste into issues, or include in test fixtures:

- database connection strings or credentials;
- raw collaboration workspace tokens or secret links;
- Instructor class capabilities, Student join capabilities, assignment capabilities, or classroom workspace capabilities;
- Instructor class capabilities, Student class-session capabilities, Student join/assignment compatibility capabilities, or classroom workspace capabilities;
- authorization headers;
- production incident snapshots containing confidential data;
- private participant identity data.

The collaboration capability model treats possession of the full secret link as read/write authorization. Classroom uses the same capability principle but separates privileges: Instructor class, Student join, assignment, and Student workspace capabilities are distinct secrets. Server code stores only token hashes and must not log raw capabilities or snapshots.
The collaboration capability model treats possession of the correct secret as authorization. Classroom separates privileges across Instructor class authority, human admission code, Student class session, assignment-specific Student workspace authority, and legacy compatibility credentials. Server code stores only hashes for high-entropy bearer capabilities and must not log raw capabilities or snapshots.

For same-device Student resume, the browser necessarily retains the one issued Student workspace capability under `kt-classroom-student-session-v1`. The Student join and assignment capabilities are never retained after successful admission. The resume envelope must never be copied into Intake state, exports, summaries, templates, URLs, analytics, logs, or error telemetry.
For the normal #312 live path, same-device Student resume stores the high-entropy **Student class-session capability** under `kt-classroom-student-session-v1` together with public class/participant/current-assignment context. The human join code is discarded after admission. The current assignment-specific workspace capability is **memory-only** and must be reacquired after reload or reassignment; it must not be written into the live resume envelope or URL. Legacy two-code sessions may still retain their issued workspace capability for compatibility. No Student resume envelope may enter Intake state, exports, summaries, templates, analytics, logs, or error telemetry.

For same-device Instructor resume, the browser retains the Instructor class capability under `kt-classroom-instructor-session-v1` together with public class metadata and the last selected public workspace ID. This credential may list and observe only its represented class through the Instructor classroom API. It must never enter Intake state, exports, summaries, templates, URLs, analytics, logs, error telemetry, or `collaboration_workspace_capabilities`.
For same-device Instructor resume, the browser retains the Instructor class capability under `kt-classroom-instructor-session-v1` together with public class metadata, the human Student join code, and the last selected public workspace ID. This credential may administer/list/observe only its represented class through Instructor classroom APIs. It must never enter Intake state, exports, summaries, templates, URLs, analytics, logs, error telemetry, or `collaboration_workspace_capabilities`.

A Student join capability alone must never enumerate class workspaces. Cross-class join/assignment combinations must fail without revealing which credential was valid. Instructor workspace listing is scoped to the represented class.
The human Student join code and Student class-session capability must never enumerate class workspaces or edit collaboration. Student own-status returns only the represented participant's assignment. Reassignment/unassign must revoke old workspace authority before destination/current access is issued, and stale old-team tokens must fail rather than map to the new team. Cross-class assignments fail without revealing unrelated class state. Instructor roster/workspace listing is scoped to the represented class.

`collaboration_workspace_capabilities` is an editable-alias path. Only explicitly allowed edit kinds may resolve through the existing collaboration PUT/PATCH handlers. Instructor observation therefore uses the separate GET-only `/api/classes/observe` authorization path and the Instructor class capability is never inserted as a workspace alias. Moving Instructor observation into the editable alias table would be a privilege escalation. See `docs/classroom-api.md`.

Expand Down
73 changes: 58 additions & 15 deletions api/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,35 @@ These rules apply to all server-only modules below `api/`.

Read `docs/classroom-api.md` and `docs/classroom-architecture.md` before modifying classroom endpoints.

The classroom authorization chain is:
The primary #312 classroom authorization chain is:

```text
Instructor class capability
-> class administration + class-scoped workspace listing

Student join capability + assignment capability
-> one authorized individual/group workspace
-> newly minted per-participant editable workspace capability
-> class administration + class-scoped roster/workspace listing
-> read-only observation + coaching

human Student join code
-> admission only
-> high-entropy Student class-session capability
-> own assignment state only
-> fresh assignment-specific classroom-student workspace capability
-> existing collaboration edit APIs
```

The legacy `Student join capability + assignment capability` chain remains an explicit two-code compatibility path.

Rules:

- Student join capability alone must never enumerate classroom workspaces.
- Assignment capability must resolve only within the active class represented by the Student join capability.
- Human join code and Student class-session capability must never enumerate classroom workspaces.
- Student class-session capability must never edit collaboration directly.
- Legacy Student join capability alone must never enumerate classroom workspaces.
- Legacy assignment capability must resolve only within the active class represented by the Student join capability.
- Instructor workspace listing must be scoped to exactly the class represented by the Instructor capability.
- Classroom workspaces reuse `collaboration_workspaces`; do not create a parallel snapshot/revision/presence engine.
- Raw bearer capabilities are returned only at creation/rotation. Persist only SHA-256 hashes.
- Classroom credentials never enter `kt-intake-full-v2`, exported Intake files, summaries, templates, or Case Study payloads.
- Individual assignment ownership uses the opaque participant UUID, never the display name.
- Live participants may be Waiting with no workspace authority; assignment is a separate server-authoritative state.

## Editable workspace alias rule

Expand Down Expand Up @@ -80,13 +89,47 @@ Any runtime change under `api/` is included in the repository test-change guard.

Classroom API changes must preserve automated coverage for:

- class-scoped Instructor listing;
- Student non-enumeration;
- class-scoped Instructor roster/workspace listing;
- Student non-enumeration and own-status-only reads;
- Waiting participant with zero workspace edit authority;
- cross-class assignment rejection;
- individual claim-once vs group sharing;
- individual occupancy vs group sharing;
- assignment revision/idempotency semantics;
- reassignment/unassign revoking old workspace authority before destination access;
- stale old-team token rejection and no Intake snapshot merge;
- credential rotation and revocation;
- classroom workspace token reuse through the existing collaboration session API;
- live and legacy classroom workspace tokens through the existing collaboration session API;
- edit-capability kind restrictions;
- legacy Standalone collaboration behavior.
- coaching class scoping, independent feedback revisions, Student read-only access, and zero Student snapshot-revision changes.
- protected Case Study Instructor class scoping, Student membership-bound access, rejection of legacy collaboration capabilities, metadata-only catalogs, POST-only payload selection, and private response headers.
- legacy two-code Classroom and Standalone collaboration behavior;
- coaching class scoping, independent feedback revisions, Student read-only access, and zero Student snapshot-revision changes;
- protected Case Study Instructor class scoping, Student membership-bound access, rejection of legacy Standalone collaboration capabilities, metadata-only catalogs, POST-only payload selection, and private response headers.


## #312 live-class capability direction

Read `docs/classroom-live-management.md` before implementing #312.

The new capability chain is intentionally different from the legacy two-code admission path:

```text
human join code (admission locator only)
-> high-entropy Student class-session capability
-> own assignment-status API only
-> assignment-specific classroom-student workspace capability
-> existing collaboration edit APIs
```

Rules:
- never accept the human join code on workspace/observer/coaching/resource APIs;
- never accept the Student class-session capability on collaboration edit APIs;
- unassigned participants have no workspace edit capability;
- on assign/reassign/unassign, revoke the previous workspace-access capability before destination access is issued;
- never remap one still-active edit token from an old workspace to a new workspace;
- no assignment operation copies or merges Intake snapshots;
- keep legacy `/api/classes/join` and `classroom_memberships` working during the additive migration;
- Student status endpoints return only that Student's assignment; they never enumerate classmates/workspaces.
- `PATCH /api/classes/participants` must revoke any existing assignment-specific workspace capability before changing the participant's workspace assignment; destination access is issued only later through `POST /api/classes/student/access`.
- live Student coaching/protected-resource authorization must validate that the participant's stored workspace-access hash still corresponds to an active, unrevoked `classroom-student` collaboration capability; the stored hash alone is not sufficient authority.
- assignment changes use optimistic `assignment_revision` checks; a conflicting update may leave old access revoked, but must never leave old access valid or remap it to a new workspace.
- stale presence cleanup after reassignment uses internal workspace identity, not the revoked bearer token.
- individual live workspaces remain single-participant; cross-class destination assignment is rejected generically.
Loading
Loading