# Mobile Agent API Requests

This file facilitates communication between Claude agents working on the mobile app (`buyerkiosk-team`) and the backend (`buyerkiosk-web`).

## How This Works

1. **Mobile Agent** writes requests to this file when it needs API information
2. **Backend Agent** checks this file and responds with implementation details
3. Both agents update the status as work progresses

---

## Pending Requests

<!-- Mobile agent: Add new requests here using the template below -->

### REQ-20260515-01: Expose break-policy `requiredBreaks` on scheduled shifts (mobile schedule endpoints)

**Status**: `pending`
**Requested**: 2026-05-15
**Mobile Feature**: Break Policy Visibility (Spec 049 — mobile follow-up, not in original spec)
**Priority**: medium

#### Context
Spec 049 (break policy system) ships compliance evaluation but no mobile surface. Managers using the Team app to view/build schedules currently have **zero visibility** into whether the schedule satisfies the store's break policy. They can schedule a 10-hour CA shift with no meal break and not see any warning until payroll surfaces the premium-pay owed.

We want the Team app to surface a small "⚠ Required: 30min unpaid meal" badge on shift tiles when the store's `breakPolicyEnabled = 1` and a scheduled shift would trigger required breaks that aren't yet satisfied. This is read-only visibility — no policy editing on mobile. The natural extension point is `lib/presentation/widgets/schedule/shift_tile.dart` and the `WeeklyDayShiftModel` / `ScheduleShiftModel` Freezed classes.

The compliance evaluator (`BreakPolicyEvaluator`) is already shipped (Phase 1, 2026-05-12). This request is just to thread its output through the mobile API.

#### Questions

1. **Which mobile endpoints should grow `requiredBreaks`?** Best candidates from the team app's API client (see `buyerkiosk-team/lib/data/datasources/schedule_repository_impl.dart`):
   - `POST /api/mobile/:typeNum/schedule/weekly` (weekly schedule summary)
   - `POST /api/mobile/:typeNum/schedule` (date-range schedule)
   - `POST /api/mobile/:typeNum/schedule/upcoming` (upcoming shifts)
   - `POST /api/mobile/:typeNum/schedule/today` (today's shift)
   - `POST /api/mobile/:typeNum/schedule/shift/:shiftId` (shift detail)
   - `POST /api/mobile/:typeNum/schedule/daily` (team-level daily — who's working today)

   Do you have a preference between (a) adding `requiredBreaks` to **all** schedule responses, or (b) only the **detail** + **today** endpoints to keep payload small on weekly/team grids? Mobile-side we're happy with either — detail-only means deferring the badge to drill-down; all-endpoints means the badge can render on every shift tile.

2. **Field shape — please confirm or adjust:**
   ```json
   "requiredBreaks": [
     {
       "type": "SCHEDULED_MEAL",
       "minDurationMinutes": 30,
       "isPaid": false,
       "compliant": false
     }
   ]
   ```
   - Should `compliant` be returned for **scheduled** (not-yet-worked) shifts? Our read of the evaluator is that it can answer "given the planned shift, would policy require breaks not yet planned in" — but for a scheduled future shift there are no actual breaks to evaluate against, so `compliant` may always be `false` for required breaks until the shift is worked. Is that the intended semantic? If so, we may want to rename to something like `satisfied: bool` (true once an actual or planned break covers the rule) — your call.
   - Any other fields you'd recommend (rule ID, premium-pay multiplier, late-window timing)?

3. **What does the response look like when `stores.breakPolicyEnabled = 0`?** Two options:
   - (a) `requiredBreaks` field omitted entirely
   - (b) `requiredBreaks: []` (empty array)

   Mobile is happy with either; (a) is slightly more compact, (b) avoids null-check ceremony in Dart. Your call.

4. **Performance / cost:** From our recon the evaluator is a pure function (~1ms per shift). For weekly/team responses returning ~50–150 shifts, is calling the evaluator per-shift acceptable, or do you want a bulk method? Happy to defer the heavier endpoints (weekly/daily) if cost is a concern.

5. **Naming convention:** Existing mobile API uses `breakType`, `unpaidBreakHours`, etc. (camelCase). Confirming `requiredBreaks` fits convention — let us know if you'd prefer a different name.

#### Backend Response
<!-- Backend agent fills this in -->
*Awaiting response*

---

## Notice: breakType field deprecated in break punches (Spec 049 T4.3)

**Status**: `implemented`
**Date**: 2026-05-12
**Affects**: BuyerKiosk Team app, BuyerKiosk Live app — clock/break endpoints

When `stores.breakPolicyEnabled = 1`, the backend now ignores any `breakType` field
sent in manual-punch (override) requests for break punches (`breakStart` / `breakEnd`).
The policy engine classifies break type automatically. The field is still accepted when
the flag is off (existing behavior preserved).

Mobile clients should stop sending `breakType` for break punches on policy-enabled stores.
A warning is logged server-side when the field is received and the flag is on.

See full update entry in `../buyerkiosk-team/docs/backend-api-updates.md` and
`../buyerkiosk-live-flutter/docs/backend-api-updates.md`.

---

### REQ-20260526-01: Include `isPublished` (and central `userId`) on every row of `POST /api/mobile/:typeNum/schedule/daily`

**Status**: `pending`
**Requested**: 2026-05-26
**Mobile Feature**: Spec 003 Schedule Visibility (Team app full-schedule, draft-leak hardening + tap-to-chat)
**Priority**: high

#### Context
The Team app's full-schedule UI (spec 003) consumes the existing
`POST /api/mobile/:typeNum/schedule/daily` endpoint for both
week-mode and month-mode rendering (the repository loops the endpoint
per day). Two adjacent issues surfaced in PR #1 code review:

1. **Draft leak (PR #1 P1 #2).** The Freezed `DailyShiftModel` previously
   defaulted `isPublished` to `true`. If the daily endpoint omits the
   field on any row, drafts leak past the Team app's defense-in-depth
   filter. We just landed the safe fix client-side: `isPublished` is now
   nullable in the model and the filter requires an explicit `true`.
   That fix means **the Team app will render EMPTY days until the
   backend includes `isPublished` on every row of `/schedule/daily`**.
2. **Tap-to-chat 403/404 (PR #1 P2 #6).** The daily endpoint's row keys
   employees by `Store.employeeId` (per-store scheduling id). The chat
   resolver (`chatChannelForUser`) expects central `uf_users.id`. When
   the two differ the chat resolver 403s/404s or opens the wrong user's
   chat. Per CLAUDE.md: "We use the global uf_users users table now"
   — so the daily endpoint should expose the central user id too.

#### Questions

1. **`isPublished` (and `hasUnpublishedChange`) on every shift row.**
   Please add these to every `shifts[]` entry in the
   `/schedule/daily` response:
   ```json
   "isPublished": true,                  // bool, NOT optional
   "hasUnpublishedChange": false,        // bool, NOT optional
   "publishedAt": "2026-05-21T10:00:00Z", // ISO8601 or null
   "updatedAt":   "2026-05-21T10:00:00Z"  // ISO8601 or null
   ```
   `isPublished` should be `(publishedAt != null)` per spec 003 T1.7.
   `hasUnpublishedChange` should be
   `(publishedAt != null) && (updatedAt > publishedAt)`.

   We already see these on the full-schedule path; just need them on
   `/schedule/daily` too, since spec 003's Team app uses the daily
   endpoint to build week/month views.

2. **Central `userId` on every shift row.** Please add a
   `userId` field (central `uf_users.id`) alongside the existing
   `employeeId` (store-scoped scheduling id):
   ```json
   {
     "shiftId": 100,
     "employeeId": 200,     // store-scheduling id (existing)
     "userId": 500,         // central uf_users.id  (NEW)
     "employeeName": "Alex",
     …
   }
   ```
   Until this lands, the Team app gracefully degrades tap-to-chat
   (disables the chat tap for rows where the central user id is
   unknown). Once you ship `userId`, the Team app will key the chat
   resolver off it.

#### Backend Response
*Awaiting response*

#### URGENT FOLLOW-UP (2026-05-27) — schedule renders EMPTY without this

PR #1 round 3 review surfaced that the **client-side draft filter currently
empties the entire schedule** when this REQ is unimplemented:

- `/schedule/daily` does not include `isPublished` in its row payload today.
- The Team app's client-side `_filterDrafts` predicate was strict `== true`
  (treat `null` as "unknown → don't show"), so every shift was being
  filtered out.
- Round-3 workaround landed in
  `lib/presentation/providers/full_team_schedule_provider.dart` —
  predicate is now `!= false` (only an explicit `false` is dropped). This
  trades the round-1 strict-fail-safe for a fail-open semantic until the
  field lands in the contract.

**Action when this REQ is implemented**: revert the predicate to `== true`
in `_filterDrafts` (and the symmetric one in `_upsertShift`). Search for
the round-3 comment markers — both call sites are explicitly labelled.

This does not change the substance of the ask; it just escalates the
priority/blast-radius. The schedule is functional NOW (`!= false` lets
all `null` rows through), but the original defense-in-depth filter
remains compromised until the backend adds `isPublished` to every row of
`/schedule/daily`.

---

### REQ-20260526-02: Grant `kiosk_{typeNum}` capability for schedule realtime in the staff-chat Ably token (OR ship a separate schedule token endpoint)

**Status**: `pending`
**Requested**: 2026-05-26
**Mobile Feature**: Spec 003-schedule-visibility — schedule realtime (Team app)
**Priority**: medium (workaround in place; UX is degraded but functional)

#### Context
Spec 003 Phase 4 wires the Team app's full-schedule provider through
Ably for realtime shift/publish/unpublish deltas. The `kiosk_{typeNum}`
channel is the team-visible feed (per ADR-3, revised — the `_manager`
channel is never subscribed from the Team app).

Backend's current staff-chat token (issued by the Ably token endpoint
the Team app currently hits via `AblyRealtimeService.connect()`) only
grants:
- `chat:*` capability (channels prefixed `chat:`)
- `alert` capability

It does NOT grant `kiosk_{typeNum}`. So when
`scheduleEventStreamProvider` calls `.subscribe('kiosk_${typeNum}')`,
Ably's server-side capability check denies the attach (silently in the
stream; loudly in the Ably dashboard / connection logs).

We have shipped a feature flag (`teamScheduleRealtimeEnabledProvider`,
default `false`) that disables the schedule subscribe attempt entirely.
The Team app currently runs schedule UX on REST fetches + manual
refresh + pull-to-refresh.

#### Questions / Asks
We're flexible on which path you take; pick whichever is cleaner:

1. **Option A (small change, broader token)**: Expand the existing
   staff-chat Ably token's capability list so that for each store the
   user can see, the token also carries
   `"kiosk_<typeNum>": ["subscribe","presence","history"]` (subscribe
   and history are what the Team app actually uses; presence is
   optional). This keeps a single token + single connection serving
   both chat and schedule channels.

2. **Option B (clean separation)**: Add a new endpoint
   `POST /api/mobile/scheduling/:typeNum/ably/token` that issues a
   schedule-only token with capability `{"kiosk_<typeNum>":
   ["subscribe","history"]}`. The Team app would connect a second
   Ably client just for schedule channels. More state to manage on
   the client but isolates schedule from chat outages.

Whichever path you choose, please confirm:
- The token TTL (we'll need to refresh on expiry — what's the current
  staff-chat TTL?)
- Whether the `kiosk_{typeNum}` channel ever uses presence (we don't
  need it but if it does we'd want presence capability too)
- The exact event names served on `kiosk_{typeNum}` — we currently
  subscribe to `shift.created`, `shift.updated`, `shift.deleted`,
  `schedule.published`, `schedule.unpublished` (per the SDD). Confirm
  these match what `AblySchedulePublisher.php` emits.

Once the backend ships either A or B, the Team app will flip
`teamScheduleRealtimeEnabledProvider` to `true` (per environment, then
globally) and realtime deltas will resume.

#### Backend Response
*Awaiting response*

---

## Request Template

```markdown
### REQ-[YYYYMMDD]-[NN]: [Brief Title]

**Status**: `pending` | `in-progress` | `answered` | `implemented`
**Requested**: [Date]
**Mobile Feature**: [Spec ID or feature name]
**Priority**: `high` | `medium` | `low`

#### Context
[What mobile feature needs this API]

#### Questions
1. [Specific question about endpoint, payload, response, etc.]
2. [Another question]

#### Backend Response
<!-- Backend agent fills this in -->
*Awaiting response*
```

---

## Completed Requests

<!-- Move answered requests here for reference -->

### [2026-05-21] Spec 003-schedule-visibility Phase 1 backend complete

**Status**: `implemented`
**Date**: 2026-05-21
**Type**: `new-endpoint` + `enhancement`
**Affects**: schedule, chat-from-schedule, real-time
**Backend Spec**: 003-schedule-visibility (Phase 1, T1.3–T1.7 landed; T1.8 = these docs)

#### Summary
The backend now broadcasts schedule mutations through Ably and exposes two new REST endpoints
(unpublish + tap-to-chat resolver). Every shift payload across 9 endpoints now includes
`hasUnpublishedChange: boolean` to drive the EDITED badge.

#### Details

**New REST endpoints:**
- `POST /api/mobile/scheduling/:typeNum/manager/schedule/unpublish` — body `{weekStart, reason?}`,
  returns `{success, unpublishId, shiftsAffected, employeesAffected, unpublishedAt}`. Manager-only.
  Does NOT fan out push/SMS — the Ably broadcast carries the signal.
- `POST /api/mobile/scheduling/:typeNum/chat/users/:userId/channel` — `userId` is `uf_users.id`.
  Returns `{channelId, isNew}`. 403 when caller + target don't share an accessible store. Mounted
  under `/api/mobile/scheduling/` (not `/api/mobile/`).

**Shift payload changes:**
- New field `hasUnpublishedChange: boolean`. Formula:
  `(publishedAt IS NOT NULL) AND (updated_at > publishedAt)`. Drives the EDITED badge.
- Companion timestamps (already present but now load-bearing): `publishedAt`, `updatedAt`
  (note camelCase in JSON; DB column is `updated_at` snake_case).
- Present on all 9 endpoints that emit shifts (schedule/upcoming/today/weekly/shift detail,
  manager dashboard, who's working, shift create/update) AND inside Ably `shift.*` events.

**Ably real-time events** (gated by `ABLY_SCHEDULING_BROADCAST_ENABLED`, default OFF):
- `shift.created`, `shift.updated`, `shift.deleted`, `schedule.published`, `schedule.unpublished`.
- Two-channel layout: `kiosk_{typeNum}` (team) + `kiosk_{typeNum}_manager` (JWT-gated server-side).
- Every payload carries an envelope: `eventId`, `serverSequence`, `serverTimestamp`,
  `actorEmployeeId`, `actorName`, `typeNum`.
- Full event-shape reference: [`docs/interfaces/scheduling-realtime-events.md`](../interfaces/scheduling-realtime-events.md).

**REST + OpenAPI reference:**
- [`docs/api/mobile-scheduling-api.md`](./mobile-scheduling-api.md) — new endpoint specs + the
  "Ably scheduling events" and "Shift object shape" appendices.
- [`docs/api/mobile-scheduling-openapi.yaml`](./mobile-scheduling-openapi.yaml) — schemas updated.

#### Mobile Action Required (Team app)
- [ ] Subscribe to `kiosk_{typeNum}` for published-only events.
- [ ] Apply the deep-map conversion (`Map<Object?, Object?>` → `Map<String, dynamic>`) before
      parsing Ably payloads. See the `ably-flutter-map-type-deep-conversion` skill — without it
      Freezed `fromJson` throws on nested envelope objects.
- [ ] Dedup by `eventId`, order by `serverSequence`.
- [ ] LWW for `shift.updated` / `shift.deleted`: drop if `event.serverTimestamp <= local.updatedAt`.
- [ ] On `schedule.published` / `schedule.unpublished`, refetch the week from REST rather than
      patching shifts inline.
- [ ] Consume `hasUnpublishedChange` to render the EDITED badge (do not recompute client-side).
- [ ] Use the new tap-to-chat resolver: `POST /api/mobile/scheduling/:typeNum/chat/users/:userId/channel`
      where `:userId` is `uf_users.id`. Treat 403 as "no shared store" and surface a friendly
      message. Reuse the returned `channelId` to open the existing chat screen.

---

### REQ-20250102-01: Employee Shift Requests API Confirmation

**Status**: `implemented`
**Requested**: 2025-01-02
**Mobile Feature**: Spec 007 - Employee Shift Requests
**Priority**: high

#### Context
Mobile app implementing time-off requests and shift swap functionality.

#### Questions
1. Are the endpoints documented in MOBILE_API.md current for shift requests?
2. What's the expected response format for POST /api/mobile/:typeNum/requests/time-off?
3. How does the shift swap approval workflow work (coworker → manager)?

#### Backend Response
Endpoints are documented. See `docs/api/MOBILE_API.md` section on Employee Requests.
Mobile app spec 007 implementation is complete and aligned with backend.

---

## Notes for Agents

### For Mobile Agent (buyerkiosk-team)
- Write requests when you need API details not in existing docs
- Be specific about payload formats, error codes, response structures
- Reference your spec ID so backend knows the context
- Check `docs/api/MOBILE_API.md` first - answer may already be there

### For Backend Agent (buyerkiosk-web)
- Check this file when starting a session
- Respond with code references (file:line) when helpful
- Update status to `in-progress` while working on response
- If API changes are needed, note them and implement
- Move completed requests to the "Completed" section
