training(lifecycle): assignments, enrollment events, and phase history - #24
Conversation
Add the Milestone 3 slice 1 substrate per ADR 0008: the expanded capability vocabulary with Trainer and Coordinator bundles and profile fields at user creation; durable trainer-to-enrollment assignments with assignment_created notices and assignment-scoped reads; append-only enrollment lifecycle events with database-mediated version changes; and effective-dated phase history validated against the pinned transition graph — with the enrollment page, the assigned-trainee view, and browser e2e proof. Closes #23 Refs #22 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DNP3dkn9c84v2YoNwx7KTg
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c8be8c8c22
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Address the four Codex review findings, each verified against the code: - gate /api/assignments/mine on view_assigned_records, the same capability every other scoped read of trainee identity takes; - stamp each phase event with the pin epoch (the latest version-change event id, database-enforced) and derive current phase and pause only from the current epoch, so state never resurrects across a version change — even back to a previously pinned version; - refuse version changes that leave the enrollment's continuing program (different_program); and - refuse version changes onto a version the trainee already has another enrollment for (already_enrolled) instead of surfacing the UNIQUE violation as a 500. Migration 0006 is amended in place; it has never shipped beyond this branch. Refs #23 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DNP3dkn9c84v2YoNwx7KTg
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f3c8f98743
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
An assignment exists to grant scoped reads, and its notice names the trainee — so a user without view_assigned_records is not assignable. Refused as trainer_lacks_capability; ADR 0008 states the rule. Refs #23 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DNP3dkn9c84v2YoNwx7KTg
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d676acdea0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ckers Address the third Codex round, each finding verified: - a phase event cannot take effect before the version change that opened its epoch was recorded — refused as out_of_order, so the stream never claims movement through a version the enrollment did not yet pin; - the enrollment detail reads everything, assignments included, from one database snapshot so a concurrent write cannot mix epochs on the page; and - the roster now carries each user's held capabilities, and the assignment picker offers only view_assigned_records holders instead of discovering ineligibility by refusal. Refs #23 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DNP3dkn9c84v2YoNwx7KTg
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8b1f04e340
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Primary Issue
Closes #23
Refs #22
Problem And Outcome
Milestone 3 slice 1: the access and lifecycle substrate the session/draft slices build on, per the settled design in #22 and ADR 0008 (added here). After merge: coordinators create role-bundled users, assign trainers to enrollments (with an in-app notice), record enrollment lifecycle events and effective-dated phase history validated against the pinned transition graph — and assigned trainers holding
view_assigned_recordsread exactly their trainees' history through the new enrollment page.Changes
0006_lifecycle.sql: user profile fields (employee identifier, title);training_assignment; append-onlyenrollment_eventandphase_eventwith database triggers — append-only enforcement, event-mediated enrollment version changes (replacing 0005's blanket repoint refusal), published-version pinning on repoint, per-kind shape checks, effective ≤ recorded, and phase references pinned to the enrollment's version (domain invariant 5)author_evaluation,review_evaluation,view_assigned_records; Trainer and Coordinator bundles;RoleBundleconsumed at user creationlifecyclemodule: status/current-phase/paused derived from the streams; enrollment events (withdraw, complete, reinstate, version change with reason); phase events with transition-graph validation (advance follows advance/skip edges, return follows remediation, restart follows restart, entry from nowhere), pause state machine, required reasons, and append-in-effective-order backfill rules; composed enrollment detail gated by capability + assignment scopeassignmentsmodule: create/end with attribution,assignment_creatednotice to the trainer in the same transaction, audit events, and the trainer's own active-assignment listtraining_httphandler group; user creation extended with role and profile fields;notify_userfor user-targeted workflow noticestests/lifecycle.rs(database invariants, capability/scope gates, graph and pause rules, effective-order backfill, API round trips) andweb/e2e/lifecycle.spec.ts(browser flow from role-bundled trainer creation through scoped read-only trainee view)Scope
Verification
Review And Merge Notes
lifecycle::record_phase_event, and the read gate inlifecycle::may_read🤖 Generated with Claude Code
https://claude.ai/code/session_01DNP3dkn9c84v2YoNwx7KTg
Generated by Claude Code