toga-ai 1.0.183 → 1.0.185

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ # toga2-view (TOGa View Frontend) — 2.0 knowledge
2
+
3
+ | Doc | Summary | Files |
4
+ |-----|---------|-------|
5
+ | [TOGa View Frontend (toga2-view) Architecture](architecture.md) | `toga2-view` is the **React/TypeScript single-page frontend** for the TOGa 2.0 platform — the customer-facing web app (home warranty / tech-support portals). | toga2-view/src/main.tsx, toga2-view/src/App.tsx, toga2-view/src/routes.tsx, toga2-view/src/api/axiosInstance.ts, toga2-view/src/api/apiFunctions.ts, toga2-view/src/utils/queryHelpers.ts, toga2-view/src/contexts/AuthContext.tsx, toga2-view/src/contexts/useUserStore.ts, toga2-view/src/hooks/useAuthenticationFlow.ts, toga2-view/vite.config.ts, toga2-view/package.json |
@@ -0,0 +1,184 @@
1
+ ---
2
+ title: TOGa View Frontend (toga2-view) Architecture
3
+ framework: "2.0"
4
+ repo: toga2-view
5
+ project: TOGa View Frontend
6
+ client: shared
7
+ type: architecture
8
+ status: active
9
+ updated: 2026-06-24
10
+ owners: [apeterson]
11
+ files:
12
+ - toga2-view/src/main.tsx
13
+ - toga2-view/src/App.tsx
14
+ - toga2-view/src/routes.tsx
15
+ - toga2-view/src/api/axiosInstance.ts
16
+ - toga2-view/src/api/apiFunctions.ts
17
+ - toga2-view/src/utils/queryHelpers.ts
18
+ - toga2-view/src/contexts/AuthContext.tsx
19
+ - toga2-view/src/contexts/useUserStore.ts
20
+ - toga2-view/src/hooks/useAuthenticationFlow.ts
21
+ - toga2-view/vite.config.ts
22
+ - toga2-view/package.json
23
+ related:
24
+ - clients/rate/profile.md
25
+ - clients/rate/features/saml-sso.md
26
+ - clients/rate/features/service-card-entitlements.md
27
+ ---
28
+
29
+ ## Summary
30
+
31
+ `toga2-view` is the **React/TypeScript single-page frontend** for the TOGa 2.0
32
+ platform — the customer-facing web app (home warranty / tech-support portals). It is
33
+ a pure client: it holds no server logic and talks exclusively to the **`api2`** backend
34
+ (the `/v2/...` JSON API) for all data and auth. It is **not** a PHP app and does **not**
35
+ build on `_underscore` directly — its only runtime dependency is `api2` (declared in
36
+ `registry.json` as `dependsOn: ["api2"]`).
37
+
38
+ **Stack:** React 18 + TypeScript, built with **Vite 7**. Routing via **React Router v7**
39
+ (`createBrowserRouter`). Server state via **TanStack React Query v5** (persisted to
40
+ `localStorage`). Client/global state via **Zustand** (user) + **React Context** (auth).
41
+ HTTP via a single **Axios** instance with auth/transaction interceptors. Styling via
42
+ **Tailwind CSS v3 + SCSS**. Shared UI primitives come from the **`@agilant/toga-blox`**
43
+ component library (npm package, versioned). Errors report to **Sentry**. Forms use
44
+ `react-hook-form`; animation via `framer-motion`; icons from FontAwesome Pro.
45
+
46
+ ## Project layout
47
+
48
+ ```
49
+ src/
50
+ ├── main.tsx # Vite entry; mounts <App/>, imports global styles
51
+ ├── App.tsx # Providers: PersistQueryClientProvider + AuthProvider + RouterProvider
52
+ ├── routes.tsx # createBrowserRouter route table + route guards
53
+ ├── api/ # Shared HTTP layer
54
+ │ ├── axiosInstance.ts # Single axios client + interceptors (auth, transactionId, 401 refresh)
55
+ │ ├── apiFunctions.ts # Generic apiGet / apiPost / apiPut / apiDelete<TData> wrappers
56
+ │ └── genericApi.ts
57
+ ├── contexts/
58
+ │ ├── AuthContext.tsx # isAuthenticated, user, login(), logout(); cross-tab sync
59
+ │ └── useUserStore.ts # Zustand user store (persisted to localStorage "zu-user")
60
+ ├── hooks/ # Shared hooks reused across pages
61
+ │ ├── useAuthenticationFlow.ts # SAML + domain-SSO orchestration (see Auth)
62
+ │ ├── useApiQuery.ts / useApiMutation.ts # React Query wrappers
63
+ │ ├── useBundleServices.ts / useActiveServices.ts # shared service-data hooks
64
+ │ └── useBreakpoint.tsx, useAnnouncements.ts, ...
65
+ ├── components/ # Reusable, non-page UI (BaseButton, BaseInputs, MobileNav, ...)
66
+ ├── pages/<Page>/ # Feature pages — see "Page convention" below
67
+ ├── utils/
68
+ │ └── queryHelpers.ts # assembleOptions(): JS query object → /v2 query string
69
+ ├── services/ # External integrations (e.g. paypalService.ts)
70
+ └── styles/ # index.scss (source) → index.css (Tailwind + SCSS output)
71
+ ```
72
+
73
+ ## Page convention (the dominant pattern)
74
+
75
+ Each feature lives under `src/pages/<PageName>/` with a **view / viewModel / api** split:
76
+
77
+ ```
78
+ pages/<PageName>/
79
+ ├── view/
80
+ │ ├── <PageName>.tsx # presentational component; consumes the viewModel hook
81
+ │ └── components/ # page-local subcomponents
82
+ ├── viewModels/ # business-logic hooks (⚠ some pages use singular "viewModel/")
83
+ │ ├── use<PageName>ViewModel.ts
84
+ │ └── DUMMYFIELDS/*.json # static labels/copy for the page
85
+ ├── api/<page>Api.ts # page-specific API calls (build options, call apiGet/apiPost)
86
+ ├── types.ts # page-local TypeScript interfaces
87
+ └── index.ts # re-exports the view component
88
+ ```
89
+
90
+ **ViewModel-hook pattern.** The view component is "dumb": it calls
91
+ `use<PageName>ViewModel()` and renders what it returns. The hook owns all state,
92
+ data-fetching, and event handlers, and returns a typed object
93
+ (`<PageName>ViewModel` interface). It composes shared hooks (`useUserStore()`,
94
+ `useBundleServices()`, `useNavigate()`, …) and the page's own `api/` functions.
95
+
96
+ > **Convention drift to know:** most pages name the folder `viewModels/` (plural), but a
97
+ > few (e.g. `CheckOut`, `Login`) use `viewModel/` (singular). When adding a page, match the
98
+ > plural form unless editing one of the existing singular ones. The hook itself is always
99
+ > `use<PageName>ViewModel` (singular "ViewModel").
100
+
101
+ ## API / data layer
102
+
103
+ All HTTP goes through the single axios instance in `src/api/axiosInstance.ts`. Never
104
+ create ad-hoc `fetch`/`axios` calls in components — go through `apiFunctions.ts`
105
+ (`apiGet`/`apiPost`/`apiPut`/`apiDelete`) and React Query.
106
+
107
+ - **Base URL is resolved per-hostname.** The instance reads the first label of
108
+ `window.location.hostname`, upper-cases it, and looks up `VITE_API_<LABEL>`, falling
109
+ back to `VITE_API`. E.g. `homewarranty.rate.com` → `VITE_API_HOMEWARRANTY`. This is why
110
+ there are many `.env.<mode>` files and per-subdomain `VITE_API_*` vars.
111
+ - **transactionId** — a fresh UUID is attached to every request's query params (request
112
+ interceptor) for end-to-end tracing.
113
+ - **Query builder** — `assembleOptions()` in `utils/queryHelpers.ts` converts a structured
114
+ JS object (`fields`, `where`, `join`/`ojoin`, `sort`) into the `api2` `/v2` query string.
115
+ Use `ojoin` (LEFT JOIN) when a related row may be absent — `join` (INNER JOIN) silently
116
+ drops parent rows that have no match.
117
+
118
+ ## Auth & tokens
119
+
120
+ - **AuthContext** holds `isAuthenticated`, `user`, `login()`, `logout()`. It persists to
121
+ `localStorage` and syncs across browser tabs via the `storage` event.
122
+ - **`useAuthenticationFlow.ts`** orchestrates two entry paths:
123
+ 1. **SAML landing** — a `?saml=<payload>` query param on load: the param is **stripped
124
+ immediately via `replaceState`** (leaving it in causes stale-payload replay on
125
+ refresh — this was a live bug), then exchanged at the API for tokens + user, after
126
+ which `login()` runs and the app navigates to `/landing`.
127
+ 2. **Domain SSO check** — if not authenticated and no `?saml=`, it queries `/domains`
128
+ for the current host; if the client is SSO-type it redirects to the IdP, otherwise
129
+ it falls through to `/login`.
130
+ - **Token storage (localStorage):** `accessToken`, `refreshToken`, `user`. Unauthenticated
131
+ requests use a **public token** auto-fetched via `POST /auth/public`. A `401` triggers a
132
+ one-shot refresh via `POST /auth/refresh`; if refresh fails, a `UserLoginRequired` window
133
+ event is dispatched to force re-login.
134
+
135
+ ## Routing & guards
136
+
137
+ Routes are defined in `src/routes.tsx` with `createBrowserRouter`:
138
+ - `/` → `AuthRedirect` (sends to `/login` or `/landing` by auth state).
139
+ - `/login` → public (redirects already-authenticated users away).
140
+ - Everything else sits behind **`PrivateRoute`** (checks `useAuth().isAuthenticated`,
141
+ shows a loading state while `useAuthenticationFlow` resolves) and is wrapped by
142
+ `MobileNavToggle`. Pages: `/home`, `/landing`, `/services`, `/activity`, `/checkout`,
143
+ `/zip-validation`, `/payment-success`, `/activation`, `/get-support`, `/select-service`,
144
+ `/create-ticket`, `/choose-plan`.
145
+
146
+ ## Build, environments & deploy
147
+
148
+ - **Tooling:** `npm run dev` (Vite dev server, port 5173), `npm run build`
149
+ (`tsc && vite build --mode production`), plus `buildBeta` / `buildGamma`. `npm run lint`
150
+ runs ESLint with `--max-warnings 0`. E2E via Cypress (`npm run cypress`).
151
+ - **Many environments:** `.env.{development,alpha,beta,gamma,sprint,stage,test,production}`.
152
+ Each supplies the `VITE_API_*` base URLs consumed by `axiosInstance.ts`. A client/brand is
153
+ selected by hostname at runtime (not a separate build), via the `VITE_API_<SUBDOMAIN>`
154
+ lookup.
155
+
156
+ ## Critical rules
157
+
158
+ - **All HTTP goes through `axiosInstance` + `apiFunctions`** — never raw `fetch`/`axios` in
159
+ components. The shared instance is what attaches auth, the transactionId, and 401-refresh.
160
+ - **Use `ojoin` (LEFT JOIN), not `join` (INNER JOIN), in `assembleOptions` queries** when a
161
+ joined row may be missing — an inner join silently drops parent records.
162
+ - **Strip the `?saml=` param before using it** (already handled in `useAuthenticationFlow`);
163
+ do not reintroduce code paths that leave it in the URL.
164
+ - **Append `T00:00:00` to date-only strings before parsing** to avoid timezone shift moving
165
+ a date back a day (a recurring display bug on service/entitlement dates).
166
+ - **Keep view components presentational** — logic, state, and side effects belong in the
167
+ `use<PageName>ViewModel` hook, not the `.tsx` view.
168
+ - **Shared hooks fan out.** A change to a shared hook (e.g. `useBundleServices`) affects every
169
+ page/viewModel that consumes it — check call sites before changing its shape.
170
+ - **Tokens live in `localStorage`** (`accessToken`/`refreshToken`/`user`). Treat them as
171
+ sensitive; never log token values or echo them into Sentry extras.
172
+
173
+ ## Client notes
174
+
175
+ **Rate** is the primary consumer of this frontend (SAML-only auth via Azure AD; warranty
176
+ service cards show Address instead of Price). See `clients/rate/profile.md` and the Rate
177
+ feature docs in `related:` for client-specific behavior layered on top of this architecture.
178
+
179
+ ## Gaps / not yet captured
180
+
181
+ - `2.0/standards/frontend.md` — no shared 2.0 React/TS standard doc exists yet; conventions
182
+ above are documented here per-repo until one is written.
183
+ - Per-page feature docs (Home, Activity, Checkout, etc.) are not individually captured; add
184
+ under `2.0/apps/toga2-view/features/` as they stabilize.
@@ -12,5 +12,6 @@
12
12
  | [NetSuite → TOGA Opportunity Sync (API Message Queue + worker2 webhook)](features/netsuite-opportunity-sync.md) | Outbound sync from NetSuite to TOGA for the record types the Forecast2 importer pulls (opportunities first; sales/items/etc. | worker2/Worker/Netsuite.php, worker2/Worker/Netsuite/Opportunity.php, worker2/Controller/Index.php, _underscore/Worker.php, test/@dave/NetSuite/api-message-queue/lib_amq_queue.js, test/@dave/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/NetSuite/api-message-queue/ue_amq_drain.js, test/@dave/NetSuite/api-message-queue/ss_amq_drain.js, test/@dave/NetSuite/api-message-queue/DEPLOY_RUNBOOK.md, test/@dave/clickup/backfill_opportunity_numbers.php, test/@dave/clickup/probe_opportunity_fields.php, test/@dave/probe_clickup_desc_match.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
13
13
  | [NetSuite → Forecast Open-Orders Sync (salesOrder webhook → OpenOrderItems)](features/netsuite-salesorder-open-orders-sync.md) | Webhook-driven, single-record port of the legacy open-orders importer (TRUE-79142). | worker2/Worker/Netsuite/SalesOrder.php, worker2/Worker/Netsuite.php, test/@dave/probe_salesorder_rest_shape.php, test/@dave/probe_open_order_lines.php, test/@dave/check_so_status.php, test/@dave/check_so_history.php, test/@dave/probe_so_rest_lines.php, test/@dave/probe_missing_oo_timing.php, test/@dave/probe_missing_oo_createdby.php, test/@dave/probe_drift_so_dates.php, worker/crons/toga2/forecast2/import_open_orders.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
14
14
  | [Startech Webhook Handler (worker2)](features/startech-webhook-handler.md) | Receives inbound webhook events from Startech (Easeedesk) and creates or updates the corresponding ticket in TOGA 2.0. | worker2/Worker/Startech.php |
15
+ | [Team Sprint Management & Reporting](features/team-sprint-management.md) | `_Worker_Team_Sprint` (file `Worker/Team/Sprint.php`) is the engine behind TOGA's internal **development-sprint process and reporting**. | worker2/Worker/Team/Sprint.php |
15
16
  | [Teams Meeting Transcript Export](features/teams-transcript-export.md) | `_Worker_Team_Transcripts` (action `Team/Transcripts/Export`) polls Microsoft Graph for Teams meeting transcripts produced by a set of organizers, classifies ea | worker2/Worker/Team/Transcripts.php, worker2/Config/production.ini, worker2/Database/TeamsTranscriptExports.sql, dbchanges2/Core/2026-06-18a - Teams Transcript Export schedule.sql |
16
17
  | [WJE Freshservice Sync (worker2)](features/wje-freshservice-sync.md) | WJE ("WJE IT", helpdesk `wje.freshservice.com`) is a **Freshservice**-based help-desk client whose tickets, contacts, assets, groups, categories, and canned res | worker2/Worker/Wje.php, _underscore/Component/Api/Wje/Wje.php, _underscore/Model/Wje/Ticket.php, _underscore/Model/Wje/TicketNote.php, _underscore/Model/Wje/Contact.php, _underscore/Model/Wje/Unit.php, _underscore/Model/Wje/TicketTeam.php, _underscore/Model/Wje/TicketCategory.php, _underscore/Model/Wje/AssetType.php, _underscore/Model/Wje/PredefinedReply.php, library/app/api/wje.php, worker/crons/toga2/wje/import_supporting_records.php, worker/crons/toga2/wje/sync_togasupply_wje.php, worker/crons/notifications/reports/wje/wje_common.php, library/app/systemmonitor/wje.php, dbchanges2/Client_Wje/2024-10-04 - WjeOnboarding.sql |
@@ -0,0 +1,203 @@
1
+ ---
2
+ title: Team Sprint Management & Reporting
3
+ framework: "2.0"
4
+ repo: worker2
5
+ project: Worker
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-06-24
10
+ owners: ["jcardinal"]
11
+ files:
12
+ - worker2/Worker/Team/Sprint.php
13
+ related:
14
+ - ../architecture.md
15
+ - ./creating-worker-actions.md
16
+ - ./clickup-work-type-automation.md
17
+ - ./clickup-project-routing.md
18
+ ---
19
+
20
+ ## Summary
21
+
22
+ `_Worker_Team_Sprint` (file `Worker/Team/Sprint.php`) is the engine behind TOGA's internal
23
+ **development-sprint process and reporting**. It is an `abstract` worker class of `static`
24
+ methods — each public method is a dispatchable worker action (`Team/Sprint/<Method>`, e.g.
25
+ `Team/Sprint/SprintEnd`) invoked on a schedule via `_Worker::runTask()`.
26
+
27
+ The feature mirrors the team's two-week (`SPRINT_LENGTH_DAYS = 14`) ClickUp-based sprint
28
+ ceremony into the `DB_TEAM` database, then produces:
29
+
30
+ - **Warning emails** before sprint-end capture and before sprint lock.
31
+ - A **Sprint Lock** snapshot that freezes each task's committed/planned state ("at lock").
32
+ - **Daily** leadership progress reports during the sprint.
33
+ - A comprehensive **Sprint End** run that scores every developer across four metrics,
34
+ emails per-developer report workbooks and a leadership summary workbook.
35
+ - **AI-generated Release Notes** (via TOGa IQ) emailed to leadership.
36
+
37
+ ClickUp is the source of truth for tasks/assignees; `DB_TEAM` is the durable store of
38
+ sprint history and computed metrics; Excel (`PhpOffice\PhpSpreadsheet`) is the report
39
+ format; `_Email` delivers everything.
40
+
41
+ ## Key files / entry points
42
+
43
+ - `Worker/Team/Sprint.php` — the entire feature (one abstract class, static methods).
44
+ - `initialize()` — registers the `Logs_True` DB connection used for ClickUp API logging.
45
+ - ClickUp config lives in class constants: `CLICKUP_TEAM_ID`, `CLICKUP_DEV_SPRINTS_FOLDER_ID`
46
+ (the folder whose child lists are the sprints), and the API access token.
47
+ - AI config constants: `AI_API_ENDPOINT` (TOGa IQ `…/api/ai/generate`), `AI_MODEL_ID`
48
+ (`bedrock/us.anthropic.claude-haiku-4-5-…`), temperature, max-tokens, timeout.
49
+ - Report distribution: `SEND_LEADERSHIP_REPORTS_TO` (leadership email list);
50
+ `SEND_EMAILS_FROM` / `SEND_EMAILS_FROM_NAME`.
51
+
52
+ > **Dispatch reminder (2.0 worker contract).** Action `Team/Sprint/SprintEnd` routes to
53
+ > `_Worker_Team_Sprint::SprintEnd`. New actions are only reachable once registered as a
54
+ > `CronJobs` row (or webhook route). See [creating-worker-actions](./creating-worker-actions.md).
55
+
56
+ ## The sprint lifecycle (and which action runs when)
57
+
58
+ A sprint runs ~14 days. Across the cycle these actions fire (scheduled as crons):
59
+
60
+ 1. **Sprint launch day, 8:30 AM** — `SendSprintEndWarnings` and `SendSprintLockWarnings`
61
+ email every developer who had a task in the latest sprint, reminding them to (a) finalize
62
+ the *previous* sprint's task data before the 9:00 AM End capture, and (b) get their *new*
63
+ sprint commitments ready to lock before the 9:45 AM launch ceremony.
64
+ 2. **~9:00 AM** — `SprintEnd` captures and scores the just-finished sprint, emails reports,
65
+ and (optionally) sends release notes.
66
+ 3. **~9:45 AM (after launch ceremony)** — `SprintLock` freezes the new sprint's plan.
67
+ 4. **Daily during the sprint** — `SprintDaily` emails leadership a progress dashboard.
68
+
69
+ Most methods accept an optional `$sprint` number; when omitted they resolve the relevant
70
+ sprint by date via `getSprint()` (which returns the sprint whose `dateStart…dateEnd` range
71
+ contains today, or, for end-of-sprint, the previous sprint).
72
+
73
+ ## Public actions (entry points)
74
+
75
+ ### `SendSprintEndWarnings()` / `SendSprintLockWarnings()`
76
+ Look up the latest `Sprints` row, find every distinct developer with a task in that sprint
77
+ (`Developers ⋈ Tasks_Developers ⋈ Tasks`), and email each a templated reminder. End-warning =
78
+ "clean up your data before the 9:00 AM End capture"; Lock-warning = "have your commitments
79
+ ready before the 9:45 AM lock; work added after lock is treated as unplanned/interrupt work."
80
+ Both send via the `Notification/Email/Send` worker action.
81
+
82
+ ### `SprintLock($sprint = null, $captureSprintLock = true, $generateSprintLockReport = true, $allowUnplannedTasks = false)`
83
+ Freezes the sprint's plan at launch time. Optionally re-captures ClickUp data first, then for
84
+ each task records the **"at lock" baseline** — `workTypeAtLock`, `sprintPointsAtLock`,
85
+ `statusAtLock` — and initializes the per-sprint tracking counters (rework events, unjustified
86
+ status/work-effort changes, in-progress time). These at-lock values are what later metrics
87
+ measure *against*. If the target sprint list cannot be found in ClickUp it emails a leadership
88
+ alert. With `$generateSprintLockReport`, emits an Excel lock report of committed/conditional/
89
+ unplanned/stretch points and tasks. When `$allowUnplannedTasks` is false, tasks appearing
90
+ after lock with an UNPLANNED work type are flagged.
91
+
92
+ ### `SprintDaily($sprint = null, …)`
93
+ In-sprint **leadership dashboard**, read-only with respect to metrics. Optionally re-captures
94
+ ClickUp data, then builds a multi-sheet workbook:
95
+ - **Dashboard** — sprint progress by *time elapsed* vs. *points completed* (and the
96
+ difference, i.e. ahead/behind pace); counts of tasks in attention statuses (Roadblocked,
97
+ On Hold, Awaiting Client, the various Review states); points/tasks by work type.
98
+ - **Team Summary** — per-developer committed/conditional/unplanned/stretch completion plus
99
+ N-sprint historical averages.
100
+ - **Stalled Task Detail** — tasks in Roadblocked / Awaiting Client / On Hold with the reason.
101
+ Emailed to the leadership list as `TeamSprintDailyReport_Sprint{N}_{date}.xlsx`.
102
+
103
+ ### `SprintEnd($sprint = null, …)`
104
+ The largest method and the heart of the feature. Phases:
105
+ 1. **Resolve** the target (previous) sprint and its developers; optionally call
106
+ `CaptureSprintEnd()` to sync the final ClickUp state into `DB_TEAM`.
107
+ 2. **Score every developer** across the four metrics below.
108
+ 3. **Per-developer report workbook** (Dashboard, Team Summary, Sprint History, Task Detail,
109
+ and a Score-Trends chart over the last `SPRINTS_BACK_FOR_AVERAGES` sprints), emailed to
110
+ each developer.
111
+ 4. **Leadership summary workbook** (team-wide dashboard, all-developer summary with a
112
+ *recommended commitment* for next sprint, and stalled-task detail), emailed to leadership.
113
+ 5. Optionally call the release-notes generator.
114
+
115
+ ### `SendReleaseNotes($sprint = null)`
116
+ Pulls every **completed** task in the sprint (`statusNow IN (…DONE)`) into a CSV of
117
+ `workType, taskType, stakeholders, applications, sprintPoints, title`, then POSTs it to the
118
+ TOGa IQ AI endpoint with a detailed system prompt that rewrites technical task titles into
119
+ business-friendly, stakeholder-grouped HTML release notes. The returned HTML (code fences
120
+ stripped) is emailed to the leadership list as "Sprint N Release Notes".
121
+
122
+ ## The scoring model (Sprint End)
123
+
124
+ Each developer receives four 0–100 sub-scores and a weighted **Final Score**:
125
+
126
+ | Metric | What it measures | Penalized by |
127
+ |--------|------------------|--------------|
128
+ | **Reliability** | Did they deliver what they committed to | Committed points/tasks not completed |
129
+ | **Quality** | Did the work hold up | Rework events |
130
+ | **Hygiene** | Was ClickUp data kept clean | Missing "Task Knowledge" / "Work Knowledge" custom fields (`hygieneMisses`) |
131
+ | **Integrity** | Were estimates honest | Unjustified work-effort (point/estimate) changes |
132
+
133
+ Key rules driven by the class constants:
134
+ - **Team-relative zero-out:** for Quality, Hygiene, and Integrity, if a developer's negative
135
+ rate is more than `FACTOR_OF_TEAM_AVERAGE_*_FOR_ZERO_SCORE` (= 4×) the team average for that
136
+ metric, that sub-score floors at 0. Scores are graded against the team, not an absolute bar.
137
+ - **Weighting:** `Final = 0.7·Reliability + 0.1·Quality + 0.1·Hygiene + 0.1·Integrity`
138
+ (`FINAL_SCORE_WEIGHT_*`). Reliability dominates.
139
+ - **Caps:** a low sub-score caps the final regardless of the others —
140
+ Reliability < 80 caps final at 80; Quality/Hygiene/Integrity < 90 cap final at 90
141
+ (`FINAL_SCORE_CAP_*`).
142
+ - **Recommended next commitment:** `PERCENT_OF_PREVIOUS_COMMITTED_TASK_AVERAGE_FOR_RECOMMENDED_COMMITMENT`
143
+ (= 0.9) × the developer's recent committed-points average, over the last 4 sprints.
144
+ - `scoreHeatMap($score)` interpolates a red→green cell color for the spreadsheets (stops
145
+ roughly at 60/70/80/90/100).
146
+
147
+ ## Data model (`DB_TEAM`)
148
+
149
+ All sprint state lives in the Team database (`_underscore::DB_TEAM`). Core tables:
150
+
151
+ - **`Sprints`** — one row per sprint: `id`, `sprint` (number), `dateStart`, `dateEnd`.
152
+ - **`Developers`** — `id`, `name`, `emailAddress`, ClickUp user identifier.
153
+ - **`Tasks`** — one row per sprint task, keyed to ClickUp. Carries the lifecycle snapshots
154
+ (`workTypeAtLock` vs `workTypeNow`, `sprintPointsAtLock` vs `sprintPointsNow`,
155
+ `statusAtLock` / `statusNow` / `statusAtEnd`), classification fields (`taskType`,
156
+ `stakeholders`, `applications`, `link`), and the computed metric counters
157
+ (`reworkEvents`, `hygieneMisses`, `unjustifiedStatusChanges`,
158
+ `unjustifiedWorkEffortChanges`, `timeInProgressThisSprint`). Task statuses use the
159
+ `_Model_Team_Task::STATUS_*` constants (e.g. `STATUS_IN__DONE`).
160
+ - **`Tasks_Developers`** — task ↔ developer assignment join table.
161
+
162
+ `CaptureSprintEnd()` and `CaptureSprintDaily()` are the private sync routines that walk the
163
+ ClickUp sprint list (paginated, `include_timl=true`), upsert `Sprints`/`Developers`/`Tasks`/
164
+ `Tasks_Developers`, count hygiene misses, and archive tasks no longer present. They differ
165
+ mainly in which snapshot columns they write (End → `statusAtEnd`; Daily → `statusNow`, and it
166
+ backfills `workTypeAtLock` the first time a task is seen).
167
+
168
+ ## External integrations
169
+
170
+ - **ClickUp API v2** — the dev-sprints **folder** (`CLICKUP_DEV_SPRINTS_FOLDER_ID`) holds one
171
+ list per sprint, named like `Sprint NN (M/D/YY - M/D/YY)`. Tasks are fetched page-by-page;
172
+ custom fields read include Work Type (committed/conditional/unplanned/stretch), Task Type,
173
+ Stakeholders, Application, and the two Knowledge fields used for hygiene scoring.
174
+ - **TOGa IQ / AI** (`api.togaiq.com/api/ai/generate`) — release-notes generation only.
175
+ - **Email** (`_Email`) — all reports and warnings; per-developer reports go to the developer,
176
+ everything else to `SEND_LEADERSHIP_REPORTS_TO`.
177
+ - **Excel** (`PhpOffice\PhpSpreadsheet`) — every report is an `.xlsx` attachment.
178
+
179
+ ## Gotchas
180
+
181
+ - **Single source file, huge methods.** `SprintEnd` alone is ~2,700 lines; `SprintLock` and
182
+ `SprintDaily` are each ~1,000. Almost all report layout/formatting is inline. Treat the
183
+ method boundaries (and the constants block at the top) as the map.
184
+ - **At-lock baseline is load-bearing.** Reliability/Integrity compare *now* vs *at lock*. If
185
+ `SprintLock` did not run (or ran without capture), the `*AtLock` columns are empty and the
186
+ end-of-sprint scores are meaningless. Lock must happen at launch.
187
+ - **Sprint list naming is a contract.** Capture matches the ClickUp sprint list by its
188
+ `Sprint NN (dates)` name / date range. A mis-named list is silently not found → leadership
189
+ gets a "sprint not found" alert instead of a report.
190
+ - **Hardcoded credentials.** ⚠ The ClickUp access token and the TOGa IQ AI API key are
191
+ currently **hardcoded as class constants** in `Sprint.php` rather than read from config.
192
+ This is a known security debt (secrets in source) — they should move to `Config/*.ini`
193
+ and the committed values should be rotated. See team coding/security standards.
194
+ - **Worker contract.** Like all 2.0 workers this must return cleanly and commit writes via
195
+ the `_Db`/transaction layer to be durable — uncommitted captures can be silently dropped.
196
+
197
+ ## Change history
198
+
199
+ - 2026-06-24 — Initial documentation of `_Worker_Team_Sprint` (`Worker/Team/Sprint.php`):
200
+ the sprint lifecycle (warnings → lock → daily → end → release notes), the four-metric
201
+ developer scoring model (reliability/quality/hygiene/integrity with weights, team-relative
202
+ zero-out, and final-score caps), the `DB_TEAM` data model, and the ClickUp / TOGa IQ / email
203
+ integrations. Flagged hardcoded ClickUp + AI credentials as security debt.
@@ -16,12 +16,12 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
16
16
  ## 2.0 framework
17
17
 
18
18
  - **_underscore** (_Underscore) _(framework core)_ — 11 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
19
- - **worker2** (Worker) — 12 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
19
+ - **worker2** (Worker) — 13 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
20
20
  - **api2** (API) — 6 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
21
21
  - **dbchanges2** (Database Changes) _(framework core)_ — 2 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
22
22
  - **toga2-supply** (TOGa Supply) — 3 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
23
23
  - **saml** (SAML SSO Gateway) — 2 doc(s) → [2.0/apps/saml/INDEX.md](2.0/apps/saml/INDEX.md)
24
- - **toga2-view** (TOGa View Frontend) — 1 doc(s) → [2.0/apps/toga2-view/INDEX.md](2.0/apps/toga2-view/INDEX.md)
24
+ - **toga2-view** (TOGa View Frontend) — 2 doc(s) → [2.0/apps/toga2-view/INDEX.md](2.0/apps/toga2-view/INDEX.md)
25
25
  - **toga2-hub** (TOGa Hub) — 2 doc(s) → [2.0/apps/toga2-hub/INDEX.md](2.0/apps/toga2-hub/INDEX.md)
26
26
  - **talos** (TOGa IQ) — 6 doc(s) → [2.0/apps/talos/INDEX.md](2.0/apps/talos/INDEX.md)
27
27
  - **voice-to-voice** (TOGa Voice) — 4 doc(s) → [2.0/apps/voice-to-voice/INDEX.md](2.0/apps/voice-to-voice/INDEX.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.183",
3
+ "version": "1.0.185",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",