Skip to content

Trouble ticket status workflow: transition events, commands, projections - #299

Merged
JamesMoulang merged 6 commits into
mainfrom
feature/tt-status
Sep 21, 2026
Merged

JamesMoulang merged 6 commits into
mainfrom
feature/tt-status

Conversation

@JamesMoulang

Copy link
Copy Markdown
Collaborator

Stacked on #298. The status workflow, re-applied from the stale feature/trouble-tickets-status branch onto the new event foundation (the stale branch pre-dated the quiz migration and could not be merged directly).

  • Six transition events, each its own strict codec so required context is enforced: TroubleTicketAssigned, Resolved (summary), Parked (why/path/intermediate actions), NeedsHelp (what was tried/why it didn't work), EquipmentSet, TitleEdited.
  • Transition rules in the projection: assigning a Todo/Needs Help/Parked ticket moves it to In Progress (assigning an In Progress ticket adds another trainer); resolving clears all assignees; needs-help unassigns just the flagging trainer; parking keeps assignees. Unknown ticket ids throw InconsistentEventError.
  • Authorization (src/commands/trouble-tickets/authorization.ts): status actions need a trainer on the ticket's equipment (or admin/super user) — an Unassigned ticket has no trainers, so only admins can act until an owner sets its equipment; title edits need an area owner; set-equipment accepts an owner of either current or target equipment.
  • Routes: command() (confirmation form + POST) for assign/resolve/park/needs-help via the shared troubleTicketActionForm factory; api() (bearer only) for create/set-equipment/edit-title. The seed create command is adapted to the value-based row hash from Record trouble tickets as events: event, command, projection, ingest driver #296.
  • Salvaged tests pass unmodified: command authorization/dedup (tests/commands/trouble-tickets/commands.test.ts) and the full transition matrix (tests/read-models/shared-state/trouble-tickets-status.test.ts).

Next in the stack: the board UI, then status-change email notifications.

🤖 Generated with Claude Code

JamesMoulang and others added 4 commits September 21, 2026 08:55
Wires runTroubleTicketIngest into the sync worker's existing 20-minute
trouble-ticket beat, guarded by the SAFETY comment: this must not deploy
until the one-time timeline backfill has run and been verified on prod,
because the append path claims each row's hash at recordedAt = now.

Repoints the /trouble-tickets POC page from the sheet cache to the
troubleTickets read model (still super-user only, still windowed to 6
months), rendering the parsed answers as labelled lines instead of the
double-stringified JSON blob. The sheet cache stays: it remains the
ingestion source.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The /trouble-tickets repoint left deps.getTroubleTicketData unread by any
production code; drop the field and its wiring (the underlying function
stays - the sync worker path and tests use it directly). Also reword the
ingest driver's comment, which still claimed it wasn't wired into the
sync worker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Re-applied from the stale feature/trouble-tickets-status branch onto the
new events foundation. Six transition events (Assigned, Resolved, Parked,
NeedsHelp, EquipmentSet, TitleEdited), each strict-codec'd with its
required context. Transition rules in the projection: assignment moves
Todo/Needs Help/Parked tickets to In Progress (multi-assignee), resolving
clears assignees, needs-help unassigns the flagging trainer, parking keeps
assignees.

Authorization: assign/resolve/park/needs-help require a trainer on the
ticket's equipment (or admin/super user); title edits require an area
owner; set-equipment accepts an owner of either the current or target
equipment. Status actions get GET confirmation forms via the shared
troubleTicketActionForm factory; create/set-equipment/edit-title are
API-only. The seed-oriented create command is adapted to the value-based
row hash.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Audit findings:
- The GET confirmation pages for assign/resolve/park/needs-help rendered
  the ticket title (submitter free text) to any logged-in member. They now
  enforce the same isTicketTrainer rule as the actions themselves, checked
  before the existence lookup so unauthorized viewers cannot probe ticket
  ids. Covered by tests.
- The seed-oriented create command claimed idempotency its time-based
  rowHash could not deliver; it now dedups by ticket id (and gets its
  missing test).
- populate-local-dev.sh seeds two tickets (one resolved to the Metal
  Lathe, one Unassigned); CLAUDE.md's API endpoint list caught up with
  reality.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JamesMoulang and others added 2 commits September 21, 2026 09:19
An old sync bug left rows with every column NULL; the sheet_id-scoped
cache replacement can never delete them (NULL never matches =), so prod
carried 542 of them forever and the ingest would warn about them every
cycle. Sweep them in the same atomic batch as the cache update.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@JamesMoulang
JamesMoulang changed the base branch from feature/tt-poller to main September 21, 2026 08:24
@JamesMoulang
JamesMoulang merged commit bfe1471 into main Sep 21, 2026
11 of 12 checks passed
@github-project-automation github-project-automation Bot moved this from Options to Done in Makespace Members App Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

1 participant