Skip to content

sessions(time): training sessions with explicit time semantics - #26

Merged
TusanHomichi merged 7 commits into
mainfrom
claude/milestone-2-org-rename-q8b2q8
Aug 29, 2026
Merged

sessions(time): training sessions with explicit time semantics#26
TusanHomichi merged 7 commits into
mainfrom
claude/milestone-2-org-rename-q8b2q8

Conversation

@TusanHomichi

@TusanHomichi TusanHomichi commented Aug 29, 2026

Copy link
Copy Markdown
Member

Primary Issue

Closes #25
Refs #22

Problem And Outcome

Milestone 3 slice 2: TrainingSession from the domain model, on the slice-1 substrate. After merge: coordinators and assigned trainers record sessions open or retroactively complete, with agency-local meaning (business date, timezone snapshot, verbatim local times) kept separate from the UTC instants that ordering, duration, and the overlap invariant reason about (PRINCIPLES.md 6) — the anchor slice 3's evaluation drafts attach to.

Changes

  • Migration 0007_sessions.sql: training_session and session_trainer, with database-enforced domain invariants — UTC end never precedes start (6); active intervals for one trainee never overlap across all their enrollments, where an open session is right-unbounded, interval ends are exclusive so contiguous handoffs are legal, and cancelled sessions release their interval (7); deliberately no per-trainee-and-date uniqueness (8); each session stamps the enrollment's pin at creation, with phases checked by a composite foreign key against the stamp (5); immutable history — an enrollment keeps its trainee, a session keeps its enrollment and version, closed sessions never change in place, membership rows never move; and a one-trainer floor
  • ADR 0009: local-time capture and resolution — entered strings stored verbatim, UTC computed once at entry server-side against the IANA tzdb bundled into the binary (jiff with tzdb-bundle-always), RFC 5545 compatible disambiguation (gap rolls forward, fold takes the earlier offset), unknown zones refused
  • New session modules, one owner per capability: session_time resolves entered local times (ADR 0009); session_membership owns trainer-membership grants — eligibility (any author_evaluation holder, per Milestone 3 design: sessions, drafts, and attribution (owner decisions needed) #22 decision 2), audited add by members and coordinators, coordinator-only remove, the floor; training_sessions owns the lifecycle — create (open or retroactively complete), edit-open, close with disposition (completed/interrupted take an end; cancelled takes none), and reads, with typed refusals ahead of every database backstop and scoped gates: create takes assign_training or authoring plus an active assignment, work takes coordination or membership, session reads extend to members without opening the enrollment history
  • Endpoints under training_http; audit kinds and Subject::Session
  • Web: sessions panel on the enrollment page (record with browser-default timezone, per-row close/interrupt/cancel, edit open sessions, coordinator member management) and a "My sessions" card for trainers with close controls on open rows
  • Tests: tests/training_sessions.rs — DST gap and fold goldens asserted against fixed-offset instants, verbatim round-trip, overlap at service and database (including across enrollments of one trainee and after cancellation releases the interval), contiguity and same-date legality, gates and membership rules, identity- and freeze-trigger refusals, close rules, API round trip with audit ordering; web/e2e/sessions.spec.ts drives the browser flow including the overlap refusal surfacing in the interface

Scope

Verification

  • Listed the exact verification commands run below
  • Added or updated tests when behavior changed
  • Added or updated an ADR when a durable decision changed
  • All fixtures and examples are invented; no real agency data
- cargo fmt --check
- cargo clippy --workspace --all-targets -- -D warnings
- cargo test --workspace            (72 passed, 0 failed)
- npm run check                     (web/: 0 errors, 0 warnings)
- npm run build                     (web/)
- npm run e2e                       (web/: 4 passed, includes new sessions.spec.ts)

Review And Merge Notes

  • Review focus: the 0007 overlap triggers (open-end COALESCE semantics, cancelled exclusion, cross-enrollment join), ADR 0009's resolution policy and its goldens, and the create/close disposition rules in training_sessions.rs
  • User or operator impact: existing installations migrate additively; the sessions panel appears on enrollment pages for coordinators and assigned trainers, and "My sessions" for author_evaluation holders; the new jiff dependency embeds the IANA timezone database in the binary

🤖 Generated with Claude Code

https://claude.ai/code/session_01DNP3dkn9c84v2YoNwx7KTg


Generated by Claude Code

Milestone 3 slice 2 per ADR 0008 and the new ADR 0009: training_session
carries the agency-local business date, IANA timezone snapshot, and
verbatim local start/end beside server-resolved UTC instants; jiff
resolves local times against the embedded tzdb with RFC 5545 compatible
disambiguation, pinned by DST gap and fold golden tests. Migration 0007
enforces domain invariants 6-8 — end never precedes start, active
intervals for one trainee never overlap (open sessions unbounded,
contiguity legal, cancelled sessions released), no per-date uniqueness —
plus pinned-version phase references and the one-trainer floor.
Session trainer membership requires author_evaluation, with member and
coordinator scoped work gates; the enrollment page gains the sessions
panel and trainers a My-sessions view, proven by browser e2e.

Closes #25
Refs #22

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DNP3dkn9c84v2YoNwx7KTg
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-29T02:17:42.468856Z 2088b16 New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5488793a80

ℹ️ 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".

Comment thread crates/consolebook-server/src/training_sessions.rs Outdated
Comment thread crates/consolebook-server/src/training_sessions.rs Outdated
Address the Codex round on #26, both verified: an empty or whitespace
local_end normalizes to no end before the disposition rules (typed
end_required instead of a constraint 500; a blank end still records an
open session), and editing an open session validates only an actual
phase change against the current pin — an unchanged phase recorded
under an earlier pin survives the edit, as migration 0007 permits.

Refs #25

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DNP3dkn9c84v2YoNwx7KTg

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2a0bbf8a9e

ℹ️ 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".

Comment thread crates/consolebook-server/migrations/0007_sessions.sql
Comment thread crates/consolebook-server/src/training_sessions.rs Outdated
Comment thread web/src/routes/+page.svelte
Address the second Codex round on #26, all three verified:

- training_session stamps the enrollment's pin at creation
  (database-enforced, immutable), invariant 5 becomes a composite
  foreign key against that stamp, and session reads present the stamped
  version — so a later enrollment version change never rewrites what a
  session said, and phase changes validate against the session's own
  version;
- initial trainer members are audited like every later access grant; and
- open sessions gain close controls on the My-sessions list, giving
  ad-hoc members the actions their membership authorizes, proven in the
  browser e2e.

Refs #25

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DNP3dkn9c84v2YoNwx7KTg

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d03b41c6a3

ℹ️ 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".

Comment thread crates/consolebook-server/migrations/0007_sessions.sql
Reject UPDATE of session_trainer identity at the database: grants are
inserted and removed through the audited service path only, so an
out-of-band UPDATE can neither transfer session access nor drain a
session past the one-trainer floor. Raw-update refusal tested.

Refs #25

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DNP3dkn9c84v2YoNwx7KTg

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4c52a87651

ℹ️ 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".

Comment thread crates/consolebook-server/Cargo.toml Outdated
Comment thread crates/consolebook-server/src/training_sessions.rs
Comment thread crates/consolebook-server/migrations/0007_sessions.sql
Comment thread crates/consolebook-server/src/training_sessions.rs Outdated
Address the fourth Codex round per the review discipline:

- jiff now always uses its bundled IANA database (no host zoneinfo
  fallback), so local-time resolution has the one-executable semantics
  ADR 0009 documents on every platform — the DST goldens prove the
  bundle resolves;
- session_created is recorded before the initial membership grants, so
  append-only ids never show access preceding the session it opens;
- the deferred-transaction SQLITE_BUSY_SNAPSHOT shape is the whole
  repository's write pattern, raised once as #27 rather than widened
  here; and
- the trainer floor at raw INSERT stays service-enforced: SQLite has no
  deferred constraints, and transactional application services are a
  named enforcement mechanism (docs/domain-model.md).

Refs #25

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DNP3dkn9c84v2YoNwx7KTg

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c841b6a4f8

ℹ️ 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".

Comment thread crates/consolebook-server/src/training_sessions.rs
Comment thread crates/consolebook-server/migrations/0007_sessions.sql
…ntity

The session module crossed the 1,000-line boundary during review, so the
slice includes its ownership reorganization (AGENTS.md): local-time
resolution (ADR 0009) moves to session_time with its own TimeRefusal,
trainer membership — eligibility, audited grants and removals, the
one-trainer floor — moves to session_membership, and training_sessions
keeps the lifecycle: gates, disposition rules, create/edit/close, reads,
and the shared domain vocabulary. No persisted or HTTP-visible impact;
the proof is the unchanged integration suite.

Migration 0007 also freezes enrollment.user_id: the overlap triggers and
session reads derive the trainee from it, so a raw reassignment could
slip past the interval invariant and rewrite whose training the sessions
recorded. An enrollment belongs to its trainee; a different trainee is a
new enrollment. Covered by a raw-UPDATE refusal test.

Refs #25.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DNP3dkn9c84v2YoNwx7KTg

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 92200d3ac5

ℹ️ 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".

Comment thread crates/consolebook-server/migrations/0007_sessions.sql
The service already refuses edits to closed sessions, but the schema
did not: a raw update could rewrite a closed session's dates, times,
phase, or disposition in place while keeping the CHECKs consistent.
Closed sessions are settled history — corrections are successor
versions or amendments (PRINCIPLES.md), arriving with the correction
machinery — so migration 0007 now refuses every update to a row whose
disposition is set. The close itself still lands: OLD.disposition is
null while it runs. Covered by a raw-UPDATE refusal test.

Refs #25.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DNP3dkn9c84v2YoNwx7KTg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Milestone 3 slice 2: training sessions with explicit time semantics

2 participants