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.
- package/knowledge/2.0/apps/toga2-view/INDEX.md +5 -0
- package/knowledge/2.0/apps/toga2-view/architecture.md +184 -0
- package/knowledge/2.0/apps/worker2/INDEX.md +1 -0
- package/knowledge/2.0/apps/worker2/features/team-sprint-management.md +203 -0
- package/knowledge/INDEX.md +2 -2
- package/package.json +1 -1
|
@@ -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.
|
package/knowledge/INDEX.md
CHANGED
|
@@ -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) —
|
|
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) —
|
|
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