<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Project xmlns="http://schemas.microsoft.com/project">
<Name>Project Maxwell.xml</Name>
<Title>Project Maxwell — TEEP Barnett Gas Release Triage Agent (Phase 1 Pilot)</Title>
<StartDate>2026-06-01T08:00:00</StartDate>
<CalendarUID>1</CalendarUID><ScheduleFromStart>1</ScheduleFromStart>
<DefaultStartTime>08:00:00</DefaultStartTime><DefaultFinishTime>17:00:00</DefaultFinishTime>
<MinutesPerDay>480</MinutesPerDay><MinutesPerWeek>2400</MinutesPerWeek>
<DaysPerMonth>20</DaysPerMonth><DurationFormat>7</DurationFormat>
<Calendars><Calendar><UID>1</UID><Name>Standard</Name><IsBaseCalendar>1</IsBaseCalendar><WeekDays>
<WeekDay><DayType>1</DayType><DayWorking>0</DayWorking></WeekDay>
<WeekDay><DayType>2</DayType><DayWorking>1</DayWorking><WorkingTimes><WorkingTime><FromTime>08:00:00</FromTime><ToTime>12:00:00</ToTime></WorkingTime><WorkingTime><FromTime>13:00:00</FromTime><ToTime>17:00:00</ToTime></WorkingTime></WorkingTimes></WeekDay>
<WeekDay><DayType>3</DayType><DayWorking>1</DayWorking><WorkingTimes><WorkingTime><FromTime>08:00:00</FromTime><ToTime>12:00:00</ToTime></WorkingTime><WorkingTime><FromTime>13:00:00</FromTime><ToTime>17:00:00</ToTime></WorkingTime></WorkingTimes></WeekDay>
<WeekDay><DayType>4</DayType><DayWorking>1</DayWorking><WorkingTimes><WorkingTime><FromTime>08:00:00</FromTime><ToTime>12:00:00</ToTime></WorkingTime><WorkingTime><FromTime>13:00:00</FromTime><ToTime>17:00:00</ToTime></WorkingTime></WorkingTimes></WeekDay>
<WeekDay><DayType>5</DayType><DayWorking>1</DayWorking><WorkingTimes><WorkingTime><FromTime>08:00:00</FromTime><ToTime>12:00:00</ToTime></WorkingTime><WorkingTime><FromTime>13:00:00</FromTime><ToTime>17:00:00</ToTime></WorkingTime></WorkingTimes></WeekDay>
<WeekDay><DayType>6</DayType><DayWorking>1</DayWorking><WorkingTimes><WorkingTime><FromTime>08:00:00</FromTime><ToTime>12:00:00</ToTime></WorkingTime><WorkingTime><FromTime>13:00:00</FromTime><ToTime>17:00:00</ToTime></WorkingTime></WorkingTimes></WeekDay>
<WeekDay><DayType>7</DayType><DayWorking>0</DayWorking></WeekDay>
</WeekDays></Calendar></Calendars><Tasks>
<Task><UID>0</UID><ID>0</ID><Name>Project Maxwell — TEEP Barnett Gas Release Triage Agent (Phase 1 Pilot)</Name><OutlineLevel>0</OutlineLevel><Summary>1</Summary></Task>
<Task><UID>1</UID><ID>1</ID><Name>SEN — Sensirion / Nubo Event Integration</Name><OutlineLevel>1</OutlineLevel><Summary>1</Summary><Start>2026-06-01T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish></Task>
<Task><UID>2</UID><ID>2</ID><Name>SEN-1 Joint Sensirion/Nubo technical kickoff + cadence + webhook-availability ask</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-01T08:00:00</Start><Finish>2026-06-01T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Run the joint working session (Taikun + Michelle + Sebastian + Darko, Nubo on the line or async via Michelle) to lock the Sensirion integration shape. Confirm: (a) does Nubo offer a native webhook to TEEP today, or must Sebastian poll Nubo and re-emit as the gateway webhook (drives whether latency is truly event-speed); (b) the device poll cadence (Q2 — Nubo authoritative; record interim default per SEN open_decision); (c) which of Nubo's three native APIs (PPM, location, kg/hr) maps to which fields of the SensirionEvent + series shapes; (d) whether device metadata reliably carries well_ids or only pad_id; (e) confirmation-status semantics (pending/confirmed/dismissed) and whether dismissed events are suppressed or still emitted. Capture all answers against teep-api.yaml SensirionEvent/SensirionDevice as the reference contract. Do NOT fabricate the cadence or the Nubo contact — record TBDs as open decisions.</Notes></Task>
<Task><UID>3</UID><ID>3</ID><Name>SEN-2 Confirm device poll cadence with Nubo (Q2)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-02T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Close the long-standing Q2: get the authoritative Sensirion device poll cadence (device_poll_seconds) from Nubo, plus the kg/hr and PPM native sample resolution and the confirmation-algorithm latency. This sets the time-series sample resolution Taikun requests/stores and validates the poll-fallback interval. Owned by Michelle (Nubo relationship) with Nubo as the authoritative source. If Nubo cannot give a firm number by the needed-by date, proceed on the interim default (see open_decision) and annotate ingested series with the assumed resolution so it can be corrected later. Do NOT invent the value.</Notes><PredecessorLink><PredecessorUID>2</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>4</UID><ID>4</ID><Name>SEN-3 Freeze Sensirion event + series + device contract against teep-api.yaml</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-02T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Lock the wire contract Maxwell consumes so bootstrap S3 drops and the live gateway webhook are byte-compatible: SensirionEvent (event_id, device_id, pad_id, pad_code, asset_path, well_ids[], route, lat, lon, start_ts, kg_per_hr, kg_per_hr_threshold_crossed_at, ppm_peak, nubo_confirmation_status, nubo_event_url), the events-detail series block (series.kg_per_hr[], series.ppm[], end_ts nullable), and SensirionDevice (device_id, pad_id, asset_path, deployed_at, device_poll_seconds, last_seen_ts). Reconcile any field deltas found in SEN-1 against the existing schema and re-lint with @redocly, re-mock with Prism. Confirm pad_code (e.g. 906003) and route carry through as the bridging keys Sierra's xlsx already uses.</Notes><PredecessorLink><PredecessorUID>2</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>5</UID><ID>5</ID><Name>SEN-4 Bootstrap-0 audit: map Sierra xlsx fields to the live event contract</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>bootstrap-0 is already ingested (emissions.alerts: 168 alerts, pad/pad_code/route/emission_id/emission_rate_kgh; 32 pad_baselines) via scripts/ingest_emissions_xlsx.py. Audit the xlsx-derived fields against the frozen SensirionEvent contract to enumerate exactly which live fields the xlsx LACKS (device_id, lat/lon, per-event PPM/kg-hr time series, asset_path, well_ids, nubo_confirmation_status) so the S3-drop spec (SEN-5) fills the gaps. This produces the gap list that the S3 generator and the adapter must cover; it also confirms the xlsx replay corpus for adapter testing.</Notes><PredecessorLink><PredecessorUID>4</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>6</UID><ID>6</ID><Name>SEN-5 TEEP S3 Sensirion drops: events + per-event series + device catalog</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Sebastian/Darko produce signed S3 JSON drops shaped EXACTLY like the frozen contract (SEN-3): (1) an events file (array of SensirionEvent) covering the Jan-2026 window and updated on a schedule; (2) per-event series files matching the events-detail series block (kg_per_hr[], ppm[]); (3) a device catalog (SensirionDevice per device: device_id, pad_id, asset_path, well_ids if available, device_poll_seconds, last_seen_ts) for the Path A registry build. This is the brief's recommended bootstrap Option A and unblocks all Taikun build work before the gateway exists. Shape identity is the hard requirement — cutover must be config-only. Depends on the gateway-workstream S3 bucket + signing being provisioned (GW-*).</Notes><PredecessorLink><PredecessorUID>4</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>5</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>7</UID><ID>7</ID><Name>SEN-6 Sensirion adapter — normalize wire payload to emissions.* (source-agnostic)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-08T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build the Taikun-side Sensirion adapter that maps a SensirionEvent (from S3 drop, webhook, or gateway poll — identical shape) into the emissions schema, extending emissions.alerts with the live fields the xlsx lacked (device_id, lat/lon, asset_path, well_ids, nubo_confirmation_status, nubo_event_url, kg_per_hr_threshold_crossed_at) via a migration. The adapter MUST be source-agnostic so bootstrap-&gt;live is a config flip. Idempotent by event_id (at-least-once dedupe). No fabricated fields — missing optional fields stay null and are logged at WARNING per the fail-and-fix-early rule. Reuses the existing ingest path patterns (scripts/ingest_emissions_xlsx.py, actionengine/engine/api/emissions_api.py).</Notes><PredecessorLink><PredecessorUID>4</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>5</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>8</UID><ID>8</ID><Name>SEN-10 Device-&gt;pad/well binding via asset_metadata.* (Strategy B, deterministic)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Bind Sensirion device IDs to canonical assets using Strategy B (reference) from 07-asset-binding-integration.md §4/§8 — NEVER fuzzy, because NUB-D-#### has zero name overlap with wells. Ingest the SEN-5 device catalog through the connector-&gt;binding pipeline, binding each device by the pad_id/well_ids/asset_path it carries to the canonical pad/well (confidence 1.0). If well_ids is absent (SEN-1 open item), bind at pad level and expand to wells via AssetHierarchyService. Write asset_metadata.asset_bindings (system='sensirion'), asset_aliases, and asset_resolution_audit. This binding is what lets a Sensirion event fan out to Cygnet/ProCount/TaskHub — so it gates the investigate phase of every downstream workstream.</Notes><PredecessorLink><PredecessorUID>6</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>9</UID><ID>9</ID><Name>SEN-7 Per-event PPM &amp; kg/hr time-series ingest + storage</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build storage + ingest for the per-event time series (series.kg_per_hr[], series.ppm[]) consumed from the events-detail shape. Add an emissions time-series table keyed on emission_id with (ts, metric, v) and sample-resolution metadata stamped from the confirmed/assumed device_poll_seconds (SEN-2). This series is the decision-trace evidence ('PPM spiked then cleared in 3 min -&gt; false positive') and the cumulative-emissions source for Sierra's reporting columns. Cache TTL per 05-security-answers §D.1 (duration of open event, max 7 days after close). Fail fast if a series is empty for an event that should have one — never synthesize points.</Notes><PredecessorLink><PredecessorUID>3</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>7</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>10</UID><ID>10</ID><Name>SEN-8 Webhook receiver — POST /webhooks/sensirion/events (HMAC-verified)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-10T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build the inbound webhook receiver on Taikun's AWS API Gateway (us-east-1) + Lambda per 05-security-answers §C.2: verify X-TEEP-Signature (HMAC-SHA256 over raw body) and X-TEEP-Timestamp (reject skew &gt;5min), dedupe by event_id (at-least-once), then hand off to the SEN-6 adapter. Return 200 ack; RFC7807 problem+json on 400/401. Built and tested against the S3 corpus replayed as signed POSTs BEFORE the live gateway exists, so it is ready for cutover. Shared-secret provisioning is a gateway-workstream dependency (GW-*).</Notes><PredecessorLink><PredecessorUID>4</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>7</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>11</UID><ID>11</ID><Name>SEN-9 Poll-fallback reconciler — GET /v1/sensirion/events?since= + detail polling</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-10T08:00:00</Start><Finish>2026-06-10T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build the backstop reconciler: every poll interval (default 5min; tune to confirmed cadence from SEN-2) call GET /v1/sensirion/events?since={last_seen} and reconcile against received webhooks to catch missed deliveries; for each open event poll GET /v1/sensirion/events/{id} every 30s for the series until closed, then every 5min. During bootstrap this reads the S3 events file as the source (config flag), then repoints to the live gateway at cutover. Idempotent against the webhook path (same event_id dedupe). Honor 429 retry-after and 5xx exponential backoff per the contract.</Notes><PredecessorLink><PredecessorUID>4</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>7</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>9</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>12</UID><ID>12</ID><Name>SEN-11 Sensirion observability + failure alerting</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-12T08:00:00</Start><Finish>2026-06-12T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Wire the Sensirion-specific metrics and alerting per 05-security-answers §E: teep_api_calls_total/latency/errors{system='sensirion'}, webhook delivery success/dup/skew-reject counts, poll-reconciler missed-webhook recoveries, and series-completeness gauge. Alert the TEEP-designated contact (channel TBD — open decision) on &gt;=5 failures in 5min against Sensirion; on persistent unavailability, flag in-flight events 'Undetected' (never silently drop, never invent classification). Emit per-event audit to emissions.event_audit by event_id + record hash.</Notes><PredecessorLink><PredecessorUID>10</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>11</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>13</UID><ID>13</ID><Name>SEN-12 Live gateway cutover — repoint webhook + poll from S3 to gateway</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-15T08:00:00</Start><Finish>2026-06-15T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Cut over from bootstrap S3 drops to the live gateway: Sebastian/Nubo emit signed POSTs to the live Taikun webhook URL, and the poll reconciler repoints from the S3 events file to GET /v1/sensirion/events on the gateway. Because the wire shape is identical (SEN-3), this is a config flip per source-system flag — no adapter/receiver code change. Validate end-to-end latency (event-speed if Nubo-&gt;TEEP is webhook-driven; capped if TEEP polls Nubo on ~30-min cadence — flag against MTTA KPI), data residency (us-east-1), HMAC, idempotency, and series completeness on the first live events. Keep S3 as a hot rollback for the operate phase.</Notes><PredecessorLink><PredecessorUID>10</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>11</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>8</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>12</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>14</UID><ID>14</ID><Name>SEN-13 Operate: live-vs-historical reconciliation + cadence/KPI true-up</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-19T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>During the operate phase, reconcile live event counts and MTTA against the Jan baseline (~7.6 events/day, ~230/month) and against any updated cadence number from Nubo if it landed after the interim default. Confirm series resolution matches the real device_poll_seconds and re-stamp/backfill if the interim default was wrong. Feed verified series + binding into the monthly-report columns. Track the 6-12 month historical pull ask (separate, for seasonal KPI firming) and hand off to reporting.</Notes><PredecessorLink><PredecessorUID>13</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>15</UID><ID>15</ID><Name>FMP — FMP / TaskHub Net-New API + Dispatch Write Path</Name><OutlineLevel>1</OutlineLevel><Summary>1</Summary><Start>2026-06-01T08:00:00</Start><Finish>2026-07-01T17:00:00</Finish></Task>
<Task><UID>16</UID><ID>16</ID><Name>FMP-1 Kickoff: confirm TaskHub write is in Phase-1 scope + auto-close policy</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-01T08:00:00</Start><Finish>2026-06-01T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Working session with Clovis, Michelle, Darko, Sebastian, Devin and Taikun to ratify PRD Open Item Q3: TaskHub POST/PATCH write IS Phase-1 scope (required for close-the-loop dispatch). Decide the close policy: does Maxwell AUTO-CLOSE tasks (PATCH status=closed once LO confirms + sensor baseline) OR does the LO always close via the TaskHub UI and a webhook fires (teep-api.yaml §3.3 notes both patterns work). Confirm the email-to-MRO fallback is acceptable if write slips past week 4. Decide whether TaskHub emits a real outbound webhook or the agent polls. ASSUMPTION until ratified: write in-scope, LO-closes-via-UI preferred (lower trust barrier), webhook preferred with 5-min poll fallback.</Notes></Task>
<Task><UID>17</UID><ID>17</ID><Name>FMP-2 Freeze the FMP/TaskHub API contract (teep-api.yaml §/v1/fmp/*)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-02T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Joint review of the existing OpenAPI 3.1 contract for the 6 FMP surfaces: GET /v1/fmp/tasks (list by pad+window+type), GET /v1/fmp/tasks/{id} (detail + update history), GET /v1/fmp/task-types, POST /v1/fmp/tasks (create dispatch), PATCH /v1/fmp/tasks/{id} (notes + close), and the POST {taikun}/taskhub/events outbound webhook. Confirm the CreateTaskRequest / PatchTaskRequest / TaskHubTask schemas match what TaskHub can produce/consume. Lock the contract as the joint build target; any deviation goes through change control. Contract is already @redocly lint-clean, Prism-mockable, Schemathesis-tested (TESTING-EVIDENCE.md) — this task validates Sebastian can implement it as-is.</Notes><PredecessorLink><PredecessorUID>16</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>18</UID><ID>18</ID><Name>FMP-3 Ratify TaskHub task-type enum + planned-emission flags with Michelle</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-02T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Resolve PRD Q + 04-system-integrations.md §3.5: confirm the canonical TaskHub task-type enum and naming convention, and which types carry planned_emission=true (used by classification to match Sensirion events to known process emissions). The current list (liquids_unloading, compression_maintenance, tank_thief_hatch_inspection, well_pump_repair, emissions_dispatch, ...) was inferred by mining 168 resolution notes — Michelle must ratify exact values. Also confirm free-text notes are English-only (per call, but confirm). DO NOT fabricate the final enum — capture Michelle's authoritative list verbatim. This feeds GET /v1/fmp/task-types and the dispatch 'type' field.</Notes><PredecessorLink><PredecessorUID>16</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>19</UID><ID>19</ID><Name>FMP-4 Bootstrap: TaskHub S3 JSON drops shaped to the contract</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-03T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Sebastian (or Michelle) exports TaskHub tasks for the pilot pads into signed S3 JSON drops shaped EXACTLY like the future GET /v1/fmp/tasks response (TaskHubTask schema): task_id, pad_id, well_id, type, status, scheduled_start/end_ts, dispatched_to + name/phone/email, notes_freetext, created_by/ts, url. Include the pad list catalog (pad_id, pad_name, lease, LO contact) for the Path-A asset registry build. This unblocks Taikun adapter dev against real TaskHub data before the gateway/API exist. Identical shape = config-only cutover. This is the FMP slice of the cross-cutting Bucket-5 bootstrap (GW/BOOT workstream owns the S3 bucket + signing).</Notes><PredecessorLink><PredecessorUID>17</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>20</UID><ID>20</ID><Name>FMP-5 Taikun TaskHub READ adapter (list/detail/task-types) against mock + S3</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-03T08:00:00</Start><Finish>2026-06-05T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build the agent-side TaskHub read adapter: taskhub.get_lo_notes / list-tasks-by-pad-window-type, get-task-detail, get-task-types. Source-agnostic per the adapter pattern — reads identical JSON from Prism mock, S3 bootstrap drops (FMP-4), or the live gateway (cutover by config). Maps TaskHubTask into Maxwell's evidence context (LO free-text notes verbatim, scheduled tasks, task type → planned_emission match). Wire as the taskhub.get_lo_notes step in the emissions_triage_close_loop workflow (03-architecture.md §workflow). Reuses the existing Maxwell context-builder fan-out; no new orchestration logic.</Notes><PredecessorLink><PredecessorUID>17</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>18</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>21</UID><ID>21</ID><Name>FMP-11 Sebastian builds FMP/TaskHub READ endpoints (list, detail, task-types)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-11T17:00:00</Finish><Duration>PT48H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>TEEP/Sebastian implements the three read endpoints against the frozen contract: GET /v1/fmp/tasks (by pad+from+to+type filter), GET /v1/fmp/tasks/{id} (detail + updates[] history), GET /v1/fmp/task-types (from FMP-3 ratified enum). TaskHub is home-built with no external API today — this is genuine net-new server work. Must emit ISO-8601 UTC timestamps, opaque IDs, cursor pagination on list, and RFC7807 errors. Sits behind the gateway (OAuth2 from GW workstream). ASSUMPTION: TaskHub's internal data model exposes dispatched_to contact (phone/email) — if not, see open decision on LO-contact source.</Notes><PredecessorLink><PredecessorUID>17</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>18</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>22</UID><ID>22</ID><Name>FMP-6 Taikun evidence-pack builder for dispatch POST body</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-08T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build the evidence-pack generator that composes the POST /v1/fmp/tasks body_markdown from collected evidence: kg/h peak, Maxwell classification + confidence, Cygnet pressure/sales/casing deltas, ProCount code + operator comment, Carte injection drop, TaskHub 'no scheduled task at start', Maxwell recommendation, and the similar-prior-event citation (e.g. 'Bewley Pad 2026-01-08, 162 kg/h, same pattern'). Populates title, pad_id, well_id, priority, linked_emission_id, linked_alert_url, assignee_role=lease_operator_on_pad, callback_webhook. Matches the worked example in teep-api.yaml CreateTaskRequest. Pulls similar-prior from the existing emissions.* historical store (168 alerts + 1677 notes) — no new model.</Notes><PredecessorLink><PredecessorUID>20</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>23</UID><ID>23</ID><Name>FMP-10 Email-to-MRO bootstrap fallback (write-not-ready path)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-10T08:00:00</Start><Finish>2026-06-10T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build the config-toggled fallback (PRD §8.1, §3.5): if TaskHub write is not live by week 4, the dispatch step emails Devin's MRO team the SAME evidence pack (FMP-6) it would have POSTed, instead of creating a TaskHub task. emissions.alerts still set cleared_location=Field, status=Open. When write turns on, flip TASKHUB_WRITE_ENABLED config to route to taskhub.create_task — no code change. This guarantees field-cleared events keep flowing Day 1 and de-risks the whole workstream from gateway/API slippage. Reuse the existing smtp.send side-effect already used for Sierra's closeout email (03-architecture.md auto_close step).</Notes><PredecessorLink><PredecessorUID>22</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>24</UID><ID>24</ID><Name>FMP-7 Taikun TaskHub WRITE adapter — POST create + Idempotency-Key handling</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-10T08:00:00</Start><Finish>2026-06-12T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build taskhub.create_task FQTN tool: POST /v1/fmp/tasks with the evidence pack, generating one Idempotency-Key (uuid-v4) per logical dispatch and replaying the same key on every retry (per §0.2 — without it, transient retries duplicate dispatch tasks). Handle 201 (store task_id/url/assigned_to onto emissions.alerts, set cleared_location=Field, status=Open awaiting field per FR-21), 409 idempotency conflict, 4xx (no retry), 429 (honour Retry-After), 5xx (exponential backoff). Log every write to emissions.event_audit (system, endpoint, payload hash, status, latency) per FR-32. Wire as the open_dispatch workflow step. Test against Prism mock first.</Notes><PredecessorLink><PredecessorUID>22</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>25</UID><ID>25</ID><Name>FMP-12 Sebastian builds FMP/TaskHub WRITE endpoints + Idempotency-Key store</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-12T08:00:00</Start><Finish>2026-06-24T17:00:00</Finish><Duration>PT72H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>TEEP/Sebastian implements POST /v1/fmp/tasks (create dispatch → 201 with task_id/url/assigned_to) and PATCH /v1/fmp/tasks/{id} (notes + status=closed). Must honour the Idempotency-Key header: store keys 24h, return the original response on same-key+same-body replay, 409 on same-key+different-body (§0.2) — without this, agent retries create duplicate dispatch tasks. Enforce ≤5 writes/min rate limit (429 + Retry-After). Map a created task to the linked_emission_id and route to the correct LO (assignee_role=lease_operator_on_pad). The actual task must appear in the TaskHub UI for the LO. This is the highest-effort, highest-risk net-new piece.</Notes><PredecessorLink><PredecessorUID>17</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>21</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>18</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>26</UID><ID>26</ID><Name>FMP-8 Taikun TaskHub WRITE adapter — PATCH notes + PATCH close</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-15T08:00:00</Start><Finish>2026-06-16T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build taskhub.patch_task (monitor-phase notes: agent_note + status=in_progress, e.g. 'sensor still 12 kg/h at 11:00 — escalating to MRO') and taskhub.close_task (PATCH status=closed with agent_resolution_summary, closed_by, closed_ts) per teep-api.yaml PATCH /v1/fmp/tasks/{id}. Both idempotent (§0.2). Close path reads the LO's final notes from the task FIRST (GET detail), maps findings to Sierra's columns (problem_identified ← LO note, equipment, equipment_component, thief_hatches_open/_repaired/_replaced), sets how_cleared='The alerts was cleared with a visit to the field.', resolution_personnel=LO name (FR-24). Respect FMP-1 close policy: if LO-closes-via-UI was chosen, agent does NOT PATCH close — it only records from the webhook. Build both code paths; toggle by config.</Notes><PredecessorLink><PredecessorUID>24</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>27</UID><ID>27</ID><Name>FMP-9 Taikun inbound task.updated webhook receiver (HMAC verify + dedupe)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-17T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build the POST {taikun}/taskhub/events receiver per teep-api.yaml /webhooks/taskhub/events. Verify X-TEEP-Signature (HMAC-SHA256 of raw body with the shared secret) and X-TEEP-Timestamp (reject skew &gt; 5 min) per §0.3. Dedupe at-least-once delivery by task_id+event+timestamp. On task.updated: parse lo_notes_added + status + work_order_id, drive the monitor→close-out phase (FR-23/FR-24) without polling. If TaskHub cannot emit a webhook (FMP-1 open item), fall back to polling GET /v1/fmp/tasks/{id} every 5 min — build the poll path as the guaranteed backstop regardless. 401 on bad signature, 400 on malformed body, both RFC7807.</Notes><PredecessorLink><PredecessorUID>26</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>28</UID><ID>28</ID><Name>FMP-13 Sebastian builds outbound task.updated webhook (HMAC-signed)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-24T08:00:00</Start><Finish>2026-06-29T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>TEEP/Sebastian implements the outbound POST {taikun}/taskhub/events webhook fired whenever an LO updates an agent-created task. Payload per teep-api.yaml: event=task.updated, task_id, linked_emission_id, status, lo_notes_added[] (ts/user/note), work_order_id. Sign with X-TEEP-Signature=HMAC-SHA256(shared_secret, raw_body) + X-TEEP-Timestamp (§0.3). At-least-once delivery with retry/backoff on Taikun 5xx. If TaskHub has NO outbound webhook infra (FMP-1 open item), this task is descoped and Taikun relies on its 5-min poll backstop (FMP-9) — so this is NOT a hard go-live blocker, but it is the difference between sub-minute and 5-min close-out detection.</Notes><PredecessorLink><PredecessorUID>25</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>29</UID><ID>29</ID><Name>FMP-14 Cutover: point Taikun adapter at live FMP gateway (config-only)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-24T08:00:00</Start><Finish>2026-06-25T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Flip the TaskHub adapter from S3 bootstrap / Prism mock to the live gateway base URL + OAuth2 client credentials, and enable TASKHUB_WRITE_ENABLED (FMP-10 flag) to route dispatch to POST instead of email. Because bootstrap JSON shape == production response shape, this is a config change, not a rewrite. Run the 4-scenario agent simulator (auto-close / dispatch / monitor / timeout) end-to-end against the LIVE FMP API. Verify create→monitor→close on a real (test) pad, idempotency under retry, webhook (or poll) close-out, and that emissions.alerts ends with all Sierra field columns populated.</Notes><PredecessorLink><PredecessorUID>24</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>26</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>27</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>21</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>25</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>30</UID><ID>30</ID><Name>FMP-15 Operate: pilot dispatch monitoring + round-trip KPI instrumentation</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-26T08:00:00</Start><Finish>2026-07-01T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>During the operate phase, instrument and watch the dispatch write path: TaskHub round-trip latency (open→close), dispatch-to-close time, write error-rate per FR-35, 24h-timeout fires, idempotency conflicts, webhook delivery vs poll-fallback ratio. Emit FR-34 alert if ≥5 write failures in 5 min. Weekly review with Devin + Michelle on whether the evidence pack is sufficient for LOs to act and whether classification→dispatch decisions are right. Capture LO free-text reconciliation gaps as Phase-2 input (the hardest classification problem). Validate against the real-data targets: of the ~27% dispatch population (~62 events/month at ~230/month), measure how many close cleanly via TaskHub vs need MRO escalation.</Notes><PredecessorLink><PredecessorUID>29</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>31</UID><ID>31</ID><Name>SCADA — Cygnet SCADA Access via Existing Systems (not direct)</Name><OutlineLevel>1</OutlineLevel><Summary>1</Summary><Start>2026-06-01T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish></Task>
<Task><UID>32</UID><ID>32</ID><Name>SCADA-1 Confirm the change: no direct CygNet API — restate the requirement</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-01T08:00:00</Start><Finish>2026-06-01T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Formally capture and circulate the change from 04-system-integrations.md §2 (which assumed TEEP wraps-and-exposes the internal CygNet API) to the new constraint: TEEP will NOT expose a direct CygNet/SCADA API; SCADA signals must be sourced via an existing intermediary. Restate the unchanged requirement: 6 curated logical signals (tubing_pressure_psi, line_pressure_psi, casing_pressure_psi, sales_rate_mcfd, compressor_status/suction_p/discharge_p, liquids-unloading flag) grounded in the 95/168 how_cleared evidence, with current-state + 4h-pre-event-series modes. Produce a one-page change note that names the three candidate intermediaries and seeds the SCADA-2 working session agenda. Assumption: the curated 6-tag subset itself is unchanged from §2.2.</Notes></Task>
<Task><UID>33</UID><ID>33</ID><Name>SCADA-2 Joint working session with Mike + Darko to pick the intermediary pipe</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-02T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Run the decision working session (per the brief's explicit ask). For each of the 3 candidate intermediaries, walk Mike + Darko through: (a) does the pipe actually carry all 6 tags (esp. casing pressure and compressor suction/discharge, which FMP/ProCount may not capture); (b) native cadence/resolution (FMP ~30min device poll per the call; ProCount daily/allocation; historian sub-minute) vs the 4h/5m series need; (c) latency from physical reading to availability; (d) governance/security bar and TEEP build effort; (e) whether liquids-unloading is computed anywhere outside CygNet. Decide a primary pipe and a fallback. Capture the decision in an ADR-style data-contract decision record. Assumption: Mike can authoritatively state what tags FMP's ~30-min poll already captures; if not, SCADA-3 resolves it.</Notes><PredecessorLink><PredecessorUID>32</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>34</UID><ID>34</ID><Name>SCADA-3 Tag-coverage probe: verify the chosen pipe actually carries all 6 signals</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-02T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Concretely verify, against the chosen pipe, which of the 6 logical tags are present and at what fidelity — do not trust the working-session assumption. If FMP/TaskHub is chosen, Sebastian confirms which device-poll fields the ~30-min cache exposes (likely tubing/line pressure and a compressor status flag; casing pressure and compressor suction/discharge are at risk). If ProCount is chosen, the IFS Merrick owner confirms which Cygnet tags land in ProCount via the documented integration. If historian/replica/export, Mike confirms the historian point list covers the 6 logical points and maps them to CygNet point IDs. Output a tag-by-tag availability matrix (present / absent / derivable) keyed to the 168-alert evidence so we know which classification patterns each gap would weaken.</Notes><PredecessorLink><PredecessorUID>33</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>35</UID><ID>35</ID><Name>SCADA-4 Adapt the Cygnet data contract to the chosen pipe (keep wire shape stable)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Update the teep-api.yaml Cygnet section + the bootstrap JSON shape so the source is the chosen intermediary, WITHOUT changing the CygnetState/CygnetSeries/liquids-unloading wire shapes Maxwell consumes (the load-bearing invariant: identical JSON shape =&gt; config-only cutover). Where the pipe cannot deliver a tag (per SCADA-3), mark it nullable in the contract and document the degradation rather than faking a value (no hardcoding / no fallback golden values). Where the pipe's native cadence is coarser than the requested step (e.g. FMP 30min vs step=5m), record server-honored step semantics so step_seconds reflects reality. Re-lint with @redocly and re-mock with Prism so the contract stays test-backed. Add a note that asset_id keys may now be the intermediary's IDs, feeding the asset-binding workstream's per-system binding strategy for 'cygnet'.</Notes><PredecessorLink><PredecessorUID>34</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>36</UID><ID>36</ID><Name>SCADA-5 Define logical-&gt;physical (-&gt;intermediary) tag map as Mike-owned config</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Per 04-system-integrations.md §2.5, the logical-tag -&gt; physical-CygNet-point mapping must be owned by Mike as a YAML/JSON config (never hardcoded in Taikun). Extend it for the intermediary indirection: logical tag (e.g. tubing_pressure_psi) -&gt; CygNet point -&gt; intermediary field/endpoint (e.g. FMP poll field name, ProCount OData property, or historian point ID). Decide pad-level vs well-level aggregation (default per §2.5: serve both). Mike authors/validates the physical mappings; Taikun provides the schema and validates it loads. This config is what makes 'if a new asset is added with proper catalog entries it just works' hold for SCADA.</Notes><PredecessorLink><PredecessorUID>34</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>37</UID><ID>37</ID><Name>SCADA-6 Bootstrap SCADA data: signed S3 JSON drops shaped as the contract</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-05T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Per the cross-cutting bootstrap model (recommended Option A), TEEP places signed S3 JSON drops of Cygnet snapshots + 4h pre-event series for recent pads, sourced from the chosen intermediary, shaped EXACTLY like the revised CygnetState/CygnetSeries contract (SCADA-4). This unblocks Taikun adapter+tool development without waiting for the gateway. Include the Cygnet asset catalog (asset_id, asset_path, parent, display_name, aliases[]) for the Path-A asset-registry build. Drops are scheduled (cadence matches the pipe). This is the SCADA slice of the broader bootstrap S3 bucket Darko sets up.</Notes><PredecessorLink><PredecessorUID>35</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>38</UID><ID>38</ID><Name>SCADA-7 Build the Taikun Cygnet adapter + the 4 cygnet read tools</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-11T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build/extend the Taikun-side Cygnet adapter and the read tools listed in 03-architecture.md §4: teep.cygnet.get_pressure_series (tubing/line/casing), teep.cygnet.get_compressor_metrics (status, suction/discharge P, sales rate), teep.cygnet.get_liquids_unloading (optional), and the state fetch used in the parallel enrichment fan-out. All tools inherit ActionEngineToolBase, return ToolResult, read against the contract (so they are source-agnostic), and resolve assets via AssetResolver / asset_metadata bindings for 'cygnet' (no hardcoded asset IDs). Read against the SCADA-6 bootstrap drops first. Adapter maps the intermediary's native response into the CygnetState/CygnetSeries shapes; missing tags surface as nulls with a logged WARNING (fail-and-fix-early, no silent fallback). Emit the teep_api_* metrics (calls/latency/errors) per §7.</Notes><PredecessorLink><PredecessorUID>35</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>37</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>39</UID><ID>39</ID><Name>SCADA-8 Wire Cygnet tools into Maxwell's parallel enrichment + classify cascade</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-12T08:00:00</Start><Finish>2026-06-12T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Plug the cygnet tools into Maxwell's investigate phase (the ai.parallel_fetch enrichment in 03-architecture.md §0.2) and confirm the deterministic classify() cascade (§8) reads the adapter outputs correctly: pressure_dropped_around (tubing+line), casing drop, sales-rate drop, compressor anomaly, liquids-unloading. Handle the degraded-resolution case from a coarse intermediary: if only ~30-min snapshots are available, ensure pressure_dropped_around still fires on a sustained drop but does NOT spuriously fire on a single coarse sample; and ensure the failure-mode in §6 ('Cygnet API down -&gt; classify Undetected if no other signal') correctly treats a missing/coarse SCADA read. Confirm Cygnet evidence appears in the classification_rationale JSONB by reference.</Notes><PredecessorLink><PredecessorUID>38</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>40</UID><ID>40</ID><Name>SCADA-9 Replay 95 Cygnet-cited Jan-2026 alerts: resolution + accuracy validation</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-15T08:00:00</Start><Finish>2026-06-16T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>The core honesty check. Replay the 95 of 168 Jan-2026 alerts whose how_cleared cites Cygnet through the new intermediary-sourced path and measure: (a) does the chosen pipe's cadence/coverage reproduce the pressure-drop / casing-drop / sales-drop / compressor signatures the original notes describe; (b) classification agreement vs the recorded resolution_type; (c) how many borderline events the coarser resolution pushes to Undetected/MRO. Quantify the latency/resolution tradeoff versus the original 4h@5m series requirement and translate it into expected impact on the &gt;=92% accuracy and 40% auto-close Phase-1 KPIs. This produces the evidence to either accept the intermediary for Phase 1 or trigger the SCADA-10 historian fallback. Note the sample is only 22 days — flag that a 6-12mo historical pull (Phase-2 nice-to-have) is needed to firm the numbers.</Notes><PredecessorLink><PredecessorUID>39</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>41</UID><ID>41</ID><Name>SCADA-10 Conditional historian/read-replica fallback design (if intermediary resolution insufficient)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-17T08:00:00</Start><Finish>2026-06-17T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>CONDITIONAL — only if SCADA-9 shows the chosen low-effort pipe (esp. FMP ~30min) degrades accuracy/auto-close below Phase-1 targets, or misses casing/compressor tags. Design the higher-fidelity fallback: a CygNet historian read endpoint, an IP-allowlisted read-only historian replica (Darko has stated direct DB is temporary-bridge-only), or a scheduled curated full-resolution tag export to S3. Same curated 6-tag subset enforced at the source; same contract wire shape so the Taikun adapter swap is config-only. Decide whether this is a Phase-1 requirement or a Phase-2 enhancement based on the KPI gap measured in SCADA-9. Assumption: a CygNet historian exists at TEEP (the platform has its own historian); confirm with Mike.</Notes><PredecessorLink><PredecessorUID>40</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>42</UID><ID>42</ID><Name>SCADA-11 Production cutover: point Cygnet adapter from bootstrap to the live pipe</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-17T08:00:00</Start><Finish>2026-06-17T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Cut the Cygnet tools from reading bootstrap S3 drops to reading the live intermediary through the TEEP gateway (OAuth2 client-credentials, RFC7807 errors, retry/back-off per the cross-cutting gateway conventions). Because the wire shape is unchanged (SCADA-4 invariant), this is a config change (endpoint + auth), not a rewrite. Smoke-test against live pads on the AWS test/demo VM, confirm latency within the per-event budget (state once + 4h series once per event, ~2x daily Sensirion event count per asset = low single digits/day per pad), and confirm metrics/error envelopes flow. Coordinate timing with the gateway workstream's OAuth2 client provisioning and the asset-registry workstream's live cygnet bindings.</Notes><PredecessorLink><PredecessorUID>38</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>40</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>43</UID><ID>43</ID><Name>SCADA-12 Operate: SCADA health monitoring, degradation alerts, tag-map drift watch</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-19T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Stand up the operate-phase guardrails for the indirect SCADA path. Alert on intermediary staleness (last-good-snapshot age exceeding the pipe's cadence + margin), missing-tag rate, and the 'Cygnet API down -&gt; capped confidence -&gt; Undetected' failure mode from §6 so a quiet SCADA outage does not silently collapse auto-close rate. Watch for tag-map drift when TEEP replaces equipment (CygNet point changes) — the Mike-owned tag_map.yaml must be updated, never patched in Taikun code. Feed SCADA-attributable Undetected escalations back to MRO and into the Phase-2 backlog (e.g. historian upgrade, or adding casing/compressor tags if the pipe gained them). Track the resolution-impact metrics from SCADA-9 against live data to validate the 22-day sample.</Notes><PredecessorLink><PredecessorUID>42</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>44</UID><ID>44</ID><Name>IFS — ProCount + Carte (IFS Merrick) Integration — production codes, comments, work orders, injection-rate enrichment + canonical asset spine</Name><OutlineLevel>1</OutlineLevel><Summary>1</Summary><Start>2026-06-01T08:00:00</Start><Finish>2026-06-22T17:00:00</Finish></Task>
<Task><UID>45</UID><ID>45</ID><Name>IFS-1 Identify and confirm the TEEP ProCount/Carte business + technical owner</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-01T08:00:00</Start><Finish>2026-06-01T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>The docs list the ProCount/Carte owner as TBD ('likely Michelle / production accounting'). At kickoff, name a single accountable TEEP owner for the IFS Merrick relationship — most likely the production-accounting / regulatory-reporting team that files RRC Form PR, possibly Michelle. Get a named technical contact who can drive IFS Foundation OData exposure and a named IFS Merrick support/integration contact. Do NOT assume Michelle owns it — confirm. This unblocks every downstream IFS task.</Notes></Task>
<Task><UID>46</UID><ID>46</ID><Name>IFS-2 Confirm IFS Foundation REST/OData catalog, entity model, and auth flow</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-03T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Working session with the TEEP ProCount owner (IFS-1) + IFS Merrick support to confirm: (a) ProCount is reachable via IFS Foundation REST/OData on the same environment TEEP will expose; (b) the OData service-document / $metadata catalog — which entities hold down/up codes, operator comments, work orders, and allocated volumes/injection; (c) the auth flow OData expects (IFS service account, token, or OAuth2) and whether it can sit behind the TEEP OAuth2 client-credentials gateway (GW-*) or needs a credential bridge. No fabrication of entity names — capture the real $metadata. Assumption to validate, not assert: OData auth is OAuth2-compatible with the gateway.</Notes><PredecessorLink><PredecessorUID>45</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>47</UID><ID>47</ID><Name>IFS-3 Carte go/no-go decision — drop separate API, serve injection_rate from ProCount</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Decide whether Carte needs its own pipe. Recommended default (from 04-§2B): Carte is a viewer on ProCount's allocation store, so drop the separate Carte API and serve injection_rate + sales_rate from the ProCount OData entities identified in IFS-2; keep teep-api.yaml /v1/carte/wells/{id}/series only as a logical interface that physically routes through the ProCount adapter. Flip to a real Carte source only if IFS confirms injection fields live exclusively in Carte. Bring the §2B question (slide-12 Q3) to the working session.</Notes><PredecessorLink><PredecessorUID>46</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>48</UID><ID>48</ID><Name>IFS-4 Capture and confirm the ProCount down/up code taxonomy + planned-emission flags</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Get the list of valid ProCount down/up code values, their human labels, and — critically — which codes are 'planned-emission' types (compressor maintenance, liquids unloading, planned venting) vs unplanned. This taxonomy drives Maxwell's Process Emissions vs Unexpected classification. Cross-check the codes against the real how_cleared notes in emissions.alerts (e.g. the COMP_DOWN '3rd stage scrubber liquid level' pattern) with Devin so the mapping reflects actual triage practice, not just the master code table. Record any codes Devin treats as planned-emission that are not obviously so.</Notes><PredecessorLink><PredecessorUID>46</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>49</UID><ID>49</ID><Name>IFS-5 TEEP delivers bootstrap S3 ProCount catalog + sample data drops (Path A unblock)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Per 04-§6 Option A and 07-§8: TEEP places signed S3 JSON drops shaped exactly like the future REST responses — (1) the ProCount well catalog (well_id, pad, well_name, lease, operator, and API number if present) for the WS-REG canonical spine, and (2) sample codes/comments/work-order/injection data covering the January 168-alert window so Taikun can build and validate the adapter against real data before the gateway exists. Identical JSON shape to teep-api.yaml means cutover is config-only. Carte catalog dedups against ProCount per IFS-3.</Notes><PredecessorLink><PredecessorUID>46</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>47</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>50</UID><ID>50</ID><Name>IFS-8 TEEP exposes ProCount (+Carte routing) OData behind the gateway</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-10T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>TEEP exposes the confirmed ProCount OData entities through the API gateway as /v1/procount/wells/{well_id}/codes, /v1/procount/work-orders, and (per IFS-3) injection_rate either as /v1/carte/wells/{well_id}/series routed through ProCount or a real Carte source. Gateway plumbing — OAuth2 client-credentials, RFC7807 problem+json errors, pagination — is the cross-cutting GW-* foundation; this task is the IFS-specific routing/curation on top. TEEP may proxy OData + rely on the Taikun adapter, or transform on the gateway side (TEEP's choice per 04-§2A.3).</Notes><PredecessorLink><PredecessorUID>46</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>47</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>51</UID><ID>51</ID><Name>IFS-7 Provide ProCount catalog as canonical asset spine to WS-REG</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Hand the ProCount well catalog (from IFS-5) to the asset-registry workstream as the canonical spine. Per 07-§8, ProCount is the production-accounting master and the recommended spine. Confirm whether ProCount exposes the 14-digit API number per well: if yes, ProCount/Cygnet/Carte all bind by Strategy A (deterministic exact-key join), so the binding ingest must pin trailing-number/API matches exactly. If only well_name, fall back to Strategy C fuzzy with the number-aware guard. Provide the catalog and the binding-strategy verdict; WS-REG owns the actual ingest into asset_metadata.{assets,aliases,bindings}.</Notes><PredecessorLink><PredecessorUID>49</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>52</UID><ID>52</ID><Name>IFS-6 Build the Taikun ProCount/Carte Atlas adapter (bootstrap + gateway-ready)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-12T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build the Taikun-side adapter that emits the exact teep-api.yaml shapes — ProCountCodes (code, code_human, start_ts, end_ts, comment, submitted_by, work_order_id), WorkOrder list, and the CarteSeries injection_rate/sales_rate series — reading from the bootstrap S3 drops first, with a config flag to switch the same adapter to the live OData gateway at cutover (no rewrite). The adapter maps IFS OData entity/field names to the canonical shapes per IFS-2. Inherit ActionEngineToolBase, FQTN tools (e.g. erp.procount.codes), return ToolResult, route any LLM-touching calls through the gateway. Apply the per-event window [event_start-24h, event_start+4h]. No hardcoded data or fallback golden numbers — fail fast if data is unavailable.</Notes><PredecessorLink><PredecessorUID>48</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>49</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>53</UID><ID>53</ID><Name>IFS-9 Cutover the ProCount/Carte adapter from bootstrap S3 to the live gateway</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-16T08:00:00</Start><Finish>2026-06-16T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Flip the IFS-6 adapter source toggle from S3 bootstrap to the live OData gateway (IFS-8). Because both paths emit identical teep-api.yaml shapes, this is a config change, not a rewrite. Run side-by-side reconciliation: for a set of January events, confirm the live gateway returns the same codes/comments/work-orders/injection as the bootstrap drops (allowing for live-data freshness). Validate auth, pagination, RFC7807 error handling, and the per-event window.</Notes><PredecessorLink><PredecessorUID>52</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>50</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>54</UID><ID>54</ID><Name>IFS-10 Validate ProCount/Carte enrichment in end-to-end triage on real events</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-19T08:00:00</Start><Finish>2026-06-22T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>With the live adapter wired into Maxwell's Investigate phase, replay a representative subset of the 56 ProCount-cited and 22 Carte-cited January alerts and confirm the ProCount codes/comments and injection-rate drops show up correctly in the evidence pack and influence classification (Process Emissions vs Unexpected). Confirm planned-emission code tagging from IFS-4 routes auto-close candidates correctly and that the evidence appears in the TaskHub dispatch body (POST /v1/fmp/tasks body_markdown 'ProCount:' / 'Carte:' lines). Feed any classification gaps back to the taxonomy.</Notes><PredecessorLink><PredecessorUID>53</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>55</UID><ID>55</ID><Name>IFS-11 Schedule nightly ProCount catalog diff to keep the registry current</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-19T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Per 04-§5.1, after the gateway is live Taikun re-ingests the ProCount catalog on a schedule (nightly diff) so the canonical asset spine stays current as wells/leases change. Wire the ProCount catalog pull into the WS-REG connector-driven binding ingest on the nightly cadence (vs on-demand — recommended default: nightly diff per 07-§10 open question). Ensure the alias-merge step reuses the live matcher's number-aware guard so ingest doesn't reproduce the perkins-14-&gt;12 class of error noted in the dry-run.</Notes><PredecessorLink><PredecessorUID>51</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>50</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>56</UID><ID>56</ID><Name>SSO — Identity &amp; SSO — Microsoft Entra ID Federation into the Taikun Platform</Name><OutlineLevel>1</OutlineLevel><Summary>1</Summary><Start>2026-06-01T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish></Task>
<Task><UID>57</UID><ID>57</ID><Name>SSO-1 SSO discovery + OIDC-vs-SAML decision with TEEP IT</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-01T08:00:00</Start><Finish>2026-06-01T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Joint working session (Taikun eng + Sahir + Darko) to confirm the federation approach. Establish that the platform already ships a hardened OIDC/OAuth2 authorization-code EntraID provider (actionengine/modules/core/auth/providers/entraid.py) and recommend OIDC as the path; confirm TEEP IT has no policy mandating SAML for external SaaS. Capture: TEEP Entra tenant GUID, who owns Enterprise-App registration + admin consent in the TotalEnergies tenant (BU vs central group IT), MFA/conditional-access posture for external apps, and whether SCIM is required. Record unknowns as open decisions — do NOT assume the tenant id or consent owner.</Notes></Task>
<Task><UID>58</UID><ID>58</ID><Name>SSO-2 File Enterprise Application / app-registration request in TEEP Entra tenant</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-02T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Sahir files the request in the TotalEnergies Entra tenant to register the Taikun/Maxwell app as a confidential OIDC client. Provide TEEP with the exact reply/redirect URI (https://&lt;taikun-prod-host&gt;/api/auth/microsoft/callback — byte-for-byte; Microsoft compares exactly), requested delegated scopes (openid, profile, email, User.Read — note msal auto-adds the reserved OIDC scopes), single-tenant audience, and a request to issue a client secret OR client certificate to Taikun. This is front-loaded because enterprise app-registration + admin consent in a large tenant routinely takes 1-3 weeks. ASSUMPTION until SSO-1 resolves it: registration is approvable within the Barnett BU; if central TotalEnergies group IT owns it, escalate immediately.</Notes><PredecessorLink><PredecessorUID>57</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>59</UID><ID>59</ID><Name>SSO-3 Define persona -&gt; platform-role mapping (MRO / HSE-reporting / admin / read-only)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-02T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Joint design of how the four Maxwell personas map onto the platform's existing three-value UserRole enum (admin/operator/viewer in actionengine/engine/services/user_management/contracts.py). Recommended default mapping: MRO (Devin) + Clovis -&gt; operator (triage + TaskHub dispatch actions); HSE-reporting (Sierra) -&gt; a reporting persona realised as viewer + a reporting/export entitlement; admin/security (Darko, Sahir) -&gt; admin; Mike -&gt; viewer/read-only; Michelle -&gt; viewer (integration-health). Decide explicitly whether a 4th role value (e.g. 'reporter') is added to UserRole or whether HSE-reporting is viewer-plus-entitlement. Output the canonical mapping table the SSO-5 code implements.</Notes><PredecessorLink><PredecessorUID>57</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>60</UID><ID>60</ID><Name>SSO-4 Create Entra security groups + emit groups claim + assign pilot users</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-03T08:00:00</Start><Finish>2026-06-03T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Sahir creates the Entra security groups corresponding to the SSO-3 personas (e.g. TAIKUN-MAXWELL-OPERATOR, -HSE-REPORTING, -ADMIN, -READONLY), assigns the 7 pilot users (Devin, Sierra, Clovis, Michelle, Mike, Darko, Sahir) to the correct groups, and configures the app registration's optional/token configuration so the ID token EMITS a 'groups' claim. Critical detail: Entra emits group OBJECT IDs (GUIDs) by default, not names — capture the 4 group GUIDs and hand them to Taikun for SSO-5. Note the Entra &gt;200-group overage behaviour (groups claim is replaced by a Graph link) and confirm pilot users are under that limit. ASSUMPTION: group GUIDs are acceptable as the mapping key (recommended over names, which are mutable).</Notes><PredecessorLink><PredecessorUID>58</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>59</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>61</UID><ID>61</ID><Name>SSO-5 Implement Entra group-object-ID -&gt; UserRole mapping in the SSO callback</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-05T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Net-new Taikun engineering: today the SSO callback (_default_role_for in actionengine/modules/core/auth/api.py) assigns a single static default role; there is no group-&gt;role mapping (IDP_ENTRAID_ALLOWED_GROUPS is only an allow/deny gate). Build a deterministic mapping from the verified ID-token 'groups' claim to UserRole using the SSO-3 table, driven by config (env var or small config block keyed on the SSO-4 group GUIDs) — never hardcode GUIDs in source. Wire it into find_or_create_sso_user so JIT-provisioned users land in the right role and re-login re-evaluates role on group change. Add the optional ALLOWED_GROUPS gate so only members of the 4 Maxwell groups can sign in. Unit-test the mapping (member of multiple groups -&gt; highest privilege; member of none -&gt; denied).</Notes><PredecessorLink><PredecessorUID>59</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>62</UID><ID>62</ID><Name>SSO-6 Decide &amp; register production redirect/reply URL and Taikun hostname</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Decide the production Taikun hostname TEEP users hit (today the demo runs at demo.taikunai.com behind CloudFront; the auth router supports per-request host derivation and an AUTH_PUBLIC_BASE_URL override for CDN/proxy cases). Decide whether the pilot uses a TEEP-branded host (e.g. taikun.totalenergies.us — the router code already anticipates multi-host) or stays on a taikunai.com host. The chosen host's /api/auth/microsoft/callback must be the exact URI registered in SSO-2. DO NOT fabricate the final hostname — confirm with Steve (Taikun) + Sahir. Set AUTH_PUBLIC_BASE_URL appropriately given the CloudFront/origin split documented for the demo VM.</Notes><PredecessorLink><PredecessorUID>57</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>63</UID><ID>63</ID><Name>SSO-8 Conditional Access / MFA review for the Taikun enterprise app</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Sahir + Darko decide and apply the conditional-access posture for sign-ins to the Taikun app: require MFA (recommended default — TotalEnergies almost certainly mandates MFA for corporate apps), device-compliance / trusted-location requirements, session lifetime, and whether external/guest accounts are blocked. Confirm the policy does not break the OIDC code flow (e.g. no policy that strips the groups claim or forces a non-supported grant). Document the applied CA policy IDs so login failures can be triaged against them.</Notes><PredecessorLink><PredecessorUID>58</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>64</UID><ID>64</ID><Name>SSO-7 Deploy IDP_ENTRAID_* config to Taikun runtime (non-prod / test VM)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-08T08:00:00</Start><Finish>2026-06-08T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Set IDP_ENTRAID_TENANT_ID (the confirmed tenant GUID), IDP_ENTRAID_CLIENT_ID, IDP_ENTRAID_CLIENT_SECRET (from SSO-2, stored as a secret not in git), IDENTITY_PROVIDERS=entraid, the group-&gt;role map config (SSO-5), IDP_ENTRAID_ALLOWED_GROUPS=&lt;4 GUIDs&gt;, and the group-mapping env vars on the Taikun TEST VM (origin-test.taikunai.com) first. Keep INTERNAL_LOGIN_ENABLED=true here so internal accounts still work during testing. Restart taikun-runtime via supervisorctl and wait ~60-75s for warm-pool init. Verify GET /api/auth/sso/providers lists entraid and the 'Sign in with Microsoft' button renders.</Notes><PredecessorLink><PredecessorUID>58</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>60</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>61</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>62</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>65</UID><ID>65</ID><Name>SSO-9 End-to-end SSO login test with all 4 personas (test users)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Joint test on the test VM: each persona logs in with a real TEEP corporate credential and verifies (a) successful redirect through login.microsoftonline.com -&gt; /api/auth/microsoft/callback, (b) JIT user row created in platform.users with the CORRECT role per SSO-3 mapping, (c) MFA prompt fires per SSO-8, (d) the user lands on the right Maxwell screens with correct entitlements (Sierra -&gt; Monthly Report/export; Devin/Clovis -&gt; Triage + dispatch; Mike/Michelle -&gt; read/integration-health; Darko/Sahir -&gt; admin), (e) login audit row written to user_audit_log, (f) a non-Maxwell-group user is denied. Use the 7 named users or dedicated test accounts mirroring their group membership. Capture screenshots/logs as acceptance evidence.</Notes><PredecessorLink><PredecessorUID>64</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>63</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>66</UID><ID>66</ID><Name>SSO-10 Production cutover: enable EntraID SSO + disable interim auth</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-12T08:00:00</Start><Finish>2026-06-12T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Promote the verified config to the production Taikun VM (demo/customer-facing): set the production IDP_ENTRAID_* values, register the production callback URI (SSO-6), set INTERNAL_LOGIN_ENABLED=false so the username/password form is removed and TEEP users can ONLY sign in via Microsoft (no shared/manual creds reach prod), keep a single break-glass internal admin documented out-of-band for emergency access. Restart taikun-runtime via supervisorctl; wait for warm pool. Do NOT reset the VM off origin/main (per the standing rule that doing so previously broke auth). Smoke-test one persona post-cutover. Provide instant rollback: re-set INTERNAL_LOGIN_ENABLED=true / unset IDENTITY_PROVIDERS and restart.</Notes><PredecessorLink><PredecessorUID>65</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>67</UID><ID>67</ID><Name>SSO-11 SSO operations runbook, offboarding, and group-membership handoff to TEEP</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-19T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Operationalise for the Operate phase: document how TEEP grants/revokes access (add/remove a user from the Entra group -&gt; next login picks up the role; removal blocks login at the allow-gate), the offboarding flow (Entra disable propagates immediately at next session expiry), client-secret/cert rotation schedule + owner (Entra secrets expire — set a renewal reminder with Sahir), and login-failure triage (map common Microsoft error codes + CA-policy denials to fixes). Hand group membership management to Sahir; hand secret-rotation calendar ownership jointly.</Notes><PredecessorLink><PredecessorUID>66</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>68</UID><ID>68</ID><Name>SSO-12 (Conditional) SCIM auto-provisioning evaluation — defer to Phase 2</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-19T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Evaluate whether SCIM auto-provisioning is needed. Recommended default: NO for the 7-user Phase-1 pilot — JIT provisioning (find_or_create_sso_user) is sufficient and the platform has no SCIM endpoint today (it would be net-new build). Document the trade-off: JIT means a user row appears on first login and role is re-evaluated each login from group claims, which covers grant/revoke adequately at pilot scale; SCIM adds value only at larger user counts or when TEEP requires deprovisioning without waiting for session expiry. If TEEP IT mandates SCIM, scope it as a Phase-2 item (net-new SCIM 2.0 endpoint + Entra provisioning connector). This task is a decision + scoping note only, not an implementation.</Notes><PredecessorLink><PredecessorUID>57</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>69</UID><ID>69</ID><Name>BEDROCK — Bedrock — LLM Inference via TEEP AWS Bedrock</Name><OutlineLevel>1</OutlineLevel><Summary>1</Summary><Start>2026-06-01T08:00:00</Start><Finish>2026-06-22T17:00:00</Finish></Task>
<Task><UID>70</UID><ID>70</ID><Name>BEDROCK-1 Confirm TEEP Bedrock account, region, and enabled Claude models</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-01T08:00:00</Start><Finish>2026-06-01T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Joint working session with Sahir (TEEP AWS account owner) and Darko to capture the three load-bearing unknowns: (a) the TEEP AWS account id that hosts Bedrock, (b) the Bedrock region (assumption to validate: us-east-1, to match the data-residency commitment in 05-security-answers.md §D.4), and (c) which Anthropic Claude model id(s) are currently enabled for InvokeModel in that account/region. DO NOT assume these values — this task exists to record them. Where a model is not yet enabled, this task produces the exact list to request in BEDROCK-2. Output a one-page facts sheet: account id, region, enabled model ARNs, and TEEP's stated data-residency requirement (in-region vs in-VPC).</Notes></Task>
<Task><UID>71</UID><ID>71</ID><Name>BEDROCK-2 Request/enable Anthropic Claude model access in TEEP Bedrock</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-03T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>On the TEEP AWS account, submit and shepherd the Bedrock 'model access' request for the Claude model(s) Maxwell needs — recommended default: a Claude 3.5 Sonnet class model as primary plus a Claude 3 Haiku class model as the fast/fallback tier (mirrors ADR-0004's TEEP routing example: taikun-chat -&gt; claude-3-5-sonnet, taikun-chat-fast -&gt; claude-3-haiku). This is the critical-path long-pole: Bedrock model access is an explicit AWS account/region approval that can take hours to days. Sahir owns the AWS-console request and any TotalEnergies internal procurement/security approval; Taikun supplies the precise model-id list and the business justification. NOTE: do not fabricate which models are pre-approved — if TEEP's enterprise AWS posture restricts which Anthropic models can be enabled, capture that and adjust the routing target.</Notes><PredecessorLink><PredecessorUID>70</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>72</UID><ID>72</ID><Name>BEDROCK-3 Provision cross-account IAM AssumeRole for the Taikun gateway</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-02T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>TEEP creates an IAM role in the TEEP AWS account that the Taikun LLM gateway can assume, per the no-static-credentials directive (05-security-answers.md §C.1, ADR-0004 §Identity &amp; secrets). Trust policy: trust the Taikun gateway's AWS principal (Taikun supplies the Taikun-side IAM principal ARN — assumption: a dedicated gateway execution role in Taikun's account 584673484283, us-east-1) plus a TEEP-issued external-id to defeat the confused-deputy problem. Permissions policy: least-privilege — `bedrock:InvokeModel` (and `InvokeModelWithResponseStream`) scoped to ONLY the model ARNs enabled in BEDROCK-2, in the one region. No bedrock:* wildcard. Sahir/Darko own the role + trust + external-id; Taikun supplies its principal ARN and confirms the external-id is delivered over a secure channel (not email body). Record the role ARN + external-id as a secret reference in the llm-config provider config (references only — the control plane rejects raw secrets).</Notes><PredecessorLink><PredecessorUID>70</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>73</UID><ID>73</ID><Name>BEDROCK-4 Add a TEEP Bedrock provider + route to the LiteLLM gateway</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-05T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>In the Taikun gateway, add a Bedrock provider entry and a TEEP per-tenant route. Concretely: in the llm-config control plane (the P2/P3 per-tenant routing that already exists — providers/models/tenant_routing tables, materialize -&gt; versioned config.generated.yaml -&gt; sudo supervisorctl restart llm-gateway, auto-rollback on failure), register a 'bedrock' provider keyed to the AssumeRole ARN + external-id reference from BEDROCK-3, and map the TEEP tenant's logical model names to the enabled Claude ARNs: taikun-chat -&gt; primary Sonnet ARN, taikun-chat-fast -&gt; Haiku ARN (LiteLLM `bedrock/&lt;model-id&gt;` with `aws_role_name`/`aws_external_id`). Set drop_params=true is already on (config.yaml) for cross-provider param tolerance. Add a fallback chain taikun-chat -&gt; [taikun-chat-fast] and, for the lag scenario, a tenant-routing fallback to the existing OpenAI route (see BEDROCK-9). Do NOT hand-edit committed config.yaml on the VM — use the materialize/publish path so it is versioned and rollback-able.</Notes><PredecessorLink><PredecessorUID>71</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>72</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>74</UID><ID>74</ID><Name>BEDROCK-5 Point Maxwell triage at the logical model name (de-hardcode gpt-4o)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-08T08:00:00</Start><Finish>2026-06-08T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Small code change in actionengine/engine/api/emissions_api.py: the Maxwell triage call already uses the gateway client (`get_async_client()`, ADR-0004) — good — but passes a concrete model string via `model = os.getenv('TAIKUN_LLM_MODEL', 'gpt-4o')`. Change it to request the logical name `taikun-chat` (Models.CHAT) so the gateway's per-tenant routing table — not the caller — decides OpenAI-vs-Bedrock. This is the single code edit needed for Bedrock cutover and is the exact intent of ADR-0004 ('callers request taikun-chat ... never a concrete model'). Confirm the triage call runs inside an `audit_context(tenant_id='TEEP', call_site='maxwell_triage', ...)` so the gateway resolves the TEEP route and the call is attributed in platform.llm_calls. No change to Maxwell's prompt, schema, or _build_alert_context.</Notes><PredecessorLink><PredecessorUID>73</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>75</UID><ID>75</ID><Name>BEDROCK-9 Fallback strategy if Bedrock model access or capacity lags</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-08T08:00:00</Start><Finish>2026-06-08T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Wire and document the degrade-don't-fail path so the pilot is never blocked on Bedrock. Two layers: (1) tenant-routing fallback — if the TEEP Bedrock route errors (model access pending from BEDROCK-2, throttling, AssumeRole failure), the gateway's fallback chain routes Maxwell to the existing direct-Anthropic/OpenAI route that is live today, and emits a metric + alert (per 05-security-answers.md §E.2 failure-notification — email primary). This keeps the recommendation engine running while Bedrock catches up. (2) Explicit governance gate — the fallback to non-TEEP-Bedrock egress must be a TEEP-approved degraded mode (it sends prompts outside the TEEP boundary), so it is OFF by default for production and only enabled with Darko's written approval as a bootstrap allowance during the access-lag window. Document the toggle (a tenant-routing flag in llm-config) and the exact conditions under which each route is active.</Notes><PredecessorLink><PredecessorUID>73</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>76</UID><ID>76</ID><Name>BEDROCK-6 Classification parity: Bedrock Claude vs OpenAI baseline on the 168-alert sample</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-10T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Run the Maxwell classifier over the labelled 168-alert Jan-2026 sample (emissions.alerts on main) twice — once via the existing OpenAI route, once via the TEEP Bedrock Claude route — and compare against Sierra's ground-truth 3-value resolution_type (Process Emissions / Unexpected / Undetected) and the 6-value internal TriageClassification. Acceptance: Bedrock route holds the &gt;=92% Phase-1 classification-accuracy KPI AND agrees with the OpenAI baseline within an agreed tolerance (recommended &lt;=3% absolute divergence on the 3-value enum). Capture per-class confusion (especially the 27% Unexpected dispatch path and the 2-3% Undetected escalation, where a regression is most costly). Because cross-provider JSON-mode/prompt behavior differs (ADR-0004 'best-effort, needs per-feature eval'), include a check that Bedrock returns parseable JSON in the exact TriageResponse schema; tune the system prompt only if needed and re-run.</Notes><PredecessorLink><PredecessorUID>74</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>77</UID><ID>77</ID><Name>BEDROCK-7 Latency + cost validation against the per-event budget</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Measure end-to-end Maxwell triage latency and per-call cost on the Bedrock route from the Taikun gateway to TEEP's Bedrock region. Acceptance: the LLM call must keep total triage inside the ~4s context-injection budget (03-architecture.md §3.1.1 / §4.2 — 'cost predictability: 1 LLM call per event'). Capture p50/p95/p99 of the Bedrock InvokeModel latency (emitted via the gateway's teep_api_latency_ms / llm-audit), the cross-region/cross-account overhead vs the direct OpenAI baseline, and per-event token + USD cost from the platform.llm_calls ledger. Project monthly cost at the observed ~230 events/month (and the auto-close mix where only ~70% need the full prompt). If p95 blows the budget, evaluate the Haiku fast-tier for the deterministic-confident cases and record a routing recommendation.</Notes><PredecessorLink><PredecessorUID>74</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>78</UID><ID>78</ID><Name>BEDROCK-8 PII redaction + data-governance verification on the Bedrock egress</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Verify that what crosses into Bedrock matches TEEP's governance posture (05-security-answers.md §D.3/§D.4). Maxwell prompts carry pad/pad_code/well identifiers, kg/h, and free-text LO notes from TaskHub. Confirm: (a) the audit ledger continues to log metadata-only — endpoint, status, latency, request/response HASH — never raw prompt/response bodies (§D.3); (b) any PII redaction step the gateway applies (ADR-0004 'redact PII before any LLM call') runs on the Bedrock path too, with a before/after sample reviewed by Darko; (c) because the call now lands in TEEP's own AWS Bedrock, prompts stay inside TEEP's governance boundary — document the exact trust boundary (Taikun us-east-1 gateway -&gt; AssumeRole -&gt; TEEP Bedrock region) and whether Bedrock's own model-invocation logging (if TEEP enables it) is acceptable to TEEP. Optionally evaluate a Bedrock Guardrail (ADR-0004 mentions guardrail_id) but treat as Phase-2 unless Darko mandates it now.</Notes><PredecessorLink><PredecessorUID>74</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>79</UID><ID>79</ID><Name>BEDROCK-10 Production cutover: TEEP tenant default route -&gt; Bedrock</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-12T08:00:00</Start><Finish>2026-06-12T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Flip the TEEP tenant's default Maxwell route from the OpenAI baseline to the TEEP Bedrock Claude model in the materialized gateway config, on the production VM, via the versioned publish path (materialize -&gt; config.generated.yaml -&gt; sudo supervisorctl restart llm-gateway -&gt; health-check -&gt; auto-rollback on failure). Run the post-cutover smoke (scripts/smoke_llm_gateway.py + one live POST /advisor/triage/{id} returning a Bedrock-generated classification) and confirm the audit ledger shows tenant=TEEP, provider=bedrock for new calls. Keep the prior config version pinned for instant rollback. This is the go-live gate for 'production LLM inference runs on TEEP Bedrock'.</Notes><PredecessorLink><PredecessorUID>76</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>77</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>78</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>75</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>80</UID><ID>80</ID><Name>BEDROCK-11 Operate: monitor Bedrock route health, cost, drift; weekly review</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-19T08:00:00</Start><Finish>2026-06-22T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>During the operate phase, watch the Bedrock route via the existing gateway observability: teep_api_latency_ms / teep_api_errors_total / triage_auto_close_ratio + the llm-audit usage/spend screens (llm-usage.html). Set an alert on Bedrock InvokeModel error-rate and on the per-day TEEP budget (the gateway already supports per-tenant daily/monthly budgets + RPM/TPM). Hold a weekly review of classification-accuracy drift vs the &gt;=92% KPI and per-event cost vs the BEDROCK-7 projection; feed any regression back as a prompt/model-tier tweak. Confirm the AssumeRole/external-id rotation expectations with Sahir.</Notes><PredecessorLink><PredecessorUID>79</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>81</UID><ID>81</ID><Name>GW — API Gateway Platform, Security Conventions &amp; Bootstrap</Name><OutlineLevel>1</OutlineLevel><Summary>1</Summary><Start>2026-06-01T08:00:00</Start><Finish>2026-06-23T17:00:00</Finish></Task>
<Task><UID>82</UID><ID>82</ID><Name>GW-1 Gateway kickoff + decision session: tech choice, auth method, bootstrap mode</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-01T08:00:00</Start><Finish>2026-06-01T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Joint working session (Darko + Sahir + Taikun eng + Steve) to lock the foundational decisions that gate everything else: (a) which gateway technology TEEP will use given none exists today (AWS API Gateway vs Apigee vs Azure APIM vs a TotalEnergies corporate standard — DO NOT assume; record outcome); (b) OAuth2 client-credentials vs mTLS as the primary Taikun auth method (default: OAuth2 client-credentials per 05-security-answers §C.1); (c) bootstrap transport — S3 signed drops vs SFTP vs read-replica (default: S3 drops per 04-system-integrations §6); (d) asset-registry Path A (Taikun ingests bootstrap catalogs) vs Path B (TEEP exposes GET /v1/assets/{id}), which hinges on whether IT-security allows source-data sharing. Output a one-page decision record.</Notes></Task>
<Task><UID>83</UID><ID>83</ID><Name>GW-2 IT-security sign-off on cross-account data sharing (S3 bootstrap + us-east-1 residency)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-03T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Sahir + Sahir's security team review and approve: TEEP placing per-system JSON in an S3 bucket readable by Taikun's AWS account (us-east-1), the data-residency posture (all processing in Taikun us-east-1, metadata-only logging, time-bounded caching, ~3yr aggregate retention per 05-security-answers §D), and confirm the no-static-creds / no-shared-accounts / no-VPN directive is satisfied by the chosen auth. This decision determines asset-registry Path A vs Path B. If IT-security blocks source-data sharing, Path B (TEEP-built GET /v1/assets/{id}) becomes mandatory. DO NOT assume the TEEP AWS account id or KMS key — capture as open decisions.</Notes><PredecessorLink><PredecessorUID>82</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>84</UID><ID>84</ID><Name>GW-4 Bootstrap drop spec: freeze JSON shapes to teep-api.yaml + drop cadence + manifest</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-02T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Taikun publishes the canonical bootstrap drop spec so every TEEP system owner produces files in the exact shape Maxwell will consume from the live gateway. Each drop is the response body of the corresponding GET endpoint (e.g. /cygnet/&lt;asset&gt;/series.json matches GET /v1/cygnet/assets/{id}/series). Defines: filename convention, per-drop manifest (drop_ts, system, record_count, schema_version, sha256), drop cadence (default: hourly for event-like data, daily for catalogs), and the per-system asset/well/pad catalog shape for Path-A registry build (04-system-integrations §5.1). This is the contract that makes cutover config-only.</Notes><PredecessorLink><PredecessorUID>82</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>85</UID><ID>85</ID><Name>GW-3 Provision S3 bootstrap bucket + signing/access for cross-account read</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Darko stands up the S3 bucket that holds the scheduled JSON drops for all 5 systems, with a cross-account read grant (bucket policy or signed-URL issuance) to Taikun's us-east-1 account. Recommended default: SSE-KMS encryption + bucket policy granting read to Taikun's account principal, or pre-signed URLs if cross-account principal grants are disallowed. Folder layout mirrors the contract: /sensirion/, /cygnet/, /procount/, /carte/, /fmp/, plus /catalog/ for the Path-A per-system asset/well/pad catalogs. DO NOT fabricate the bucket name or account ids — leave as configurable and record in the decision log.</Notes><PredecessorLink><PredecessorUID>83</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>86</UID><ID>86</ID><Name>GW-13 Taikun inbound webhook receiver (API Gateway + Lambda, us-east-1) — HMAC verify + replay guard</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-08T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Taikun builds the inbound side of the webhook contract per 05-security-answers §C.2: AWS API Gateway + Lambda in us-east-1 exposing the two endpoints ({taikun}/sensirion/events, {taikun}/taskhub/events). Verifies X-TEEP-Signature (HMAC-SHA256 over raw body), rejects timestamp skew &gt; 5 min, applies a nonce/event-id replay guard (idempotent by event_id / task_id), returns 2xx on accept and 401 on invalid signature (matching teep-api.yaml's declared 202/401). Hands verified payloads to Maxwell's triage entry. Provides TEEP the exact signature scheme to implement (GW-12), or adopts a TotalEnergies standard if one exists.</Notes><PredecessorLink><PredecessorUID>84</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>87</UID><ID>87</ID><Name>GW-7 Stand up TEEP gateway platform shell (routing, TLS, base URL, /v1 versioning)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Darko stands up the chosen gateway technology (per GW-1) as the single ingress for all 17 endpoints + 2 webhooks: HTTPS/TLS termination, /v1 path versioning, route stubs for each operationId in teep-api.yaml, and the production base URL (teep-api.yaml currently carries placeholder gateway.teep.example.com — replace with the real host once known; DO NOT fabricate). No business logic yet — just the platform that the auth, idempotency, and error-envelope layers attach to.</Notes><PredecessorLink><PredecessorUID>82</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>88</UID><ID>88</ID><Name>GW-5 TEEP produces first real bootstrap drops for all 5 systems + catalogs</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-10T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Each TEEP system owner generates the first real JSON drops into the S3 bucket per GW-4's spec: Sensirion/TaskHub from Sebastian, Cygnet from Mike (via the existing intermediary, not direct SCADA), ProCount/Carte from the IFS Merrick owner. Sensirion can seed from Sierra's xlsx (already proven as bootstrap-0). Each drop includes its per-system asset/well/pad catalog for the Path-A registry build. Scope is the 22-day Jan-2026 window to start; extend toward 6-12 months as historical pull lands (see ASK / KPI workstream).</Notes><PredecessorLink><PredecessorUID>85</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>84</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>89</UID><ID>89</ID><Name>GW-12 HMAC-SHA256 webhook signing (TEEP side) + shared-secret provisioning</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-10T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>TEEP implements outbound webhook signing for the two TEEP-&gt;Taikun webhooks (POST /webhooks/sensirion/events, POST /webhooks/taskhub/events) per 04-system-integrations §0.3 + teep-api.yaml: X-TEEP-Signature: sha256=&lt;lowercase hex of HMAC-SHA256(shared_secret, raw_body)&gt; and X-TEEP-Timestamp (ISO-8601 UTC). Provisions a per-environment shared secret jointly with Taikun (out-of-band) and defines a rotation cadence. Sensirion-event signing is on TEEP's relay of Nubo events (Sebastian connects to the Sensirion server); TaskHub task.updated signing is on TaskHub's outbound (Michelle/Sebastian). DO NOT hardcode the secret anywhere — provision via vault/parameter store.</Notes><PredecessorLink><PredecessorUID>87</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>90</UID><ID>90</ID><Name>GW-14 RFC7807 problem+json error envelope across all endpoints</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-10T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Darko (with each system owner for endpoint-specific semantics) implements the uniform RFC7807 application/problem+json error envelope per 04-system-integrations §0.4 + teep-api.yaml components.responses: every non-2xx returns {type, title, status, detail, instance, trace_id}. Standard codes Taikun handles: 400 validation, 401 unauthenticated, 403 forbidden, 422 unprocessable/idempotency-conflict, 429 rate-limited (with Retry-After), 5xx server. trace_id is a correlatable ULID/UUID so failures can be matched against Taikun's audit + TEEP's gateway logs.</Notes><PredecessorLink><PredecessorUID>87</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>91</UID><ID>91</ID><Name>GW-8 OAuth2 client-credentials: token endpoint + Taikun client provisioning + scopes</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-10T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Darko configures the OAuth2 client-credentials grant (RFC 6749 §4.4) on the gateway: token endpoint, issuer, short-lived rotating access tokens (default TTL 1h per 04-system-integrations §0.1), and the two scopes from teep-api.yaml (emissions.read, emissions.write). Provisions the dedicated Taikun client (client_id/secret delivered out-of-band, never static long-lived). No shared accounts, no IP allowlist required, no VPN. DO NOT fabricate the token endpoint URL or issuer — record once provisioned. If GW-1 chose mTLS instead, this task swaps to TEEP-issued client cert + gateway pinning.</Notes><PredecessorLink><PredecessorUID>87</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>92</UID><ID>92</ID><Name>GW-6 Taikun S3 bootstrap poller + source adapters (one code path, shape-stable)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-11T08:00:00</Start><Finish>2026-06-16T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Taikun builds the S3 poller and per-system source adapters that read the bootstrap drops and feed Maxwell exactly as the live gateway will. Critical design constraint: the adapter consumes the frozen JSON shape (GW-4) so the only difference at cutover is the source (S3 object vs gateway GET). Validates each drop against schema_version, deduplicates by manifest sha256, and emits the same internal records the live path will. Includes the Path-A catalog ingest into asset_metadata.{assets,aliases,bindings} (reusing the proven R2Q schema).</Notes><PredecessorLink><PredecessorUID>84</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>88</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>93</UID><ID>93</ID><Name>GW-10 Idempotency-Key 24h replay store on the gateway (write endpoints)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-12T08:00:00</Start><Finish>2026-06-16T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Darko (with TaskHub owner Sebastian, since TaskHub is the only write target) implements the Idempotency-Key store per 04-system-integrations §0.2 + teep-api.yaml components.parameters.IdempotencyKey: every POST/PATCH carries an Idempotency-Key (uuid-v4); within 24h the same key + same body hash returns the original response (same status, same body) without re-running the action; same key + different body returns 409/422 idempotency-conflict (per the spec's declared response). Storage keyed by (client, key) with body-hash; 24h TTL eviction. This prevents duplicate TaskHub dispatch tasks on retries.</Notes><PredecessorLink><PredecessorUID>87</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>91</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>94</UID><ID>94</ID><Name>GW-9 Taikun OAuth token client + secret rotation handling</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-12T08:00:00</Start><Finish>2026-06-12T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Taikun builds/points the platform's auth client at TEEP's token endpoint: fetches client-credentials bearer tokens, caches until expiry, auto-refreshes, and handles 401/403 per 05-security-answers §E.2 (stop, rotate, retry once, then alert — no silent retries on possibly-revoked creds). Supports secret rotation without redeploy. Wires the Taikun LLM/gateway audit so token acquisition is observable.</Notes><PredecessorLink><PredecessorUID>91</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>95</UID><ID>95</ID><Name>GW-11 Taikun idempotency-key generation + safe-replay retry policy</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-16T08:00:00</Start><Finish>2026-06-16T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Taikun ensures one uuid-v4 Idempotency-Key is generated per logical write attempt and replayed verbatim on every transient retry of the same write (never a new key for the same logical action). Implements the write-side retry policy from 04-system-integrations §3.4 / §0.2: retry 5xx with exponential backoff, never retry a 4xx, dedupe webhook-driven closes, and cap write rate at &lt;=5/min globally. Records the key in emissions.event_audit.</Notes><PredecessorLink><PredecessorUID>93</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>96</UID><ID>96</ID><Name>GW-15 Contract conformance run: re-point Prism/Schemathesis/simulator at the live gateway</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-16T08:00:00</Start><Finish>2026-06-17T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Joint conformance pass: flip the existing test harness (Redocly lint already clean; Prism mock; Schemathesis ~300 cases; 4-scenario simulator) from --base-url mock to the live TEEP gateway base URL. Validates that auth (Bearer), Idempotency-Key replay (GW-10/11), HMAC webhook verify (GW-12/13), and RFC7807 errors (GW-14) all behave on real infrastructure exactly as the contract declares. This is the gate that proves cutover is config-only.</Notes><PredecessorLink><PredecessorUID>91</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>93</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>89</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>86</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>90</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>97</UID><ID>97</ID><Name>GW-16 Cutover: flip Maxwell source from S3 bootstrap to live gateway (config-only)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-19T08:00:00</Start><Finish>2026-06-22T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Execute the config-only cutover: change Maxwell's source adapters from the S3 bootstrap poller (GW-6) to the live gateway endpoints (base URL + OAuth client). Because the JSON shapes are identical (GW-4), this is a config/flag change, not a rewrite. Run a parallel/shadow period where both sources feed and outputs are compared on the Jan sample to confirm parity, then cut the live endpoint over. Keep S3 bootstrap as instant rollback.</Notes><PredecessorLink><PredecessorUID>92</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>96</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>98</UID><ID>98</ID><Name>GW-17 Gateway observability + failure-notification wiring (operate)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-23T08:00:00</Start><Finish>2026-06-23T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Wire the operate-phase observability per 05-security-answers §E: Taikun emits teep_api_calls_total / teep_api_latency_ms / teep_api_errors_total Prometheus metrics + per-call rows in emissions.event_audit (metadata only, no raw bodies per §D.3). Configure the persistent-failure alert (&gt;=5 failures in 5 min against one system) to the TEEP-designated contact/channel (email default; PagerDuty/Opsgenie/Teams if TEEP provides a key — DO NOT assume; open decision). Confirm log retention (90d -&gt; Glacier 2y).</Notes><PredecessorLink><PredecessorUID>97</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>99</UID><ID>99</ID><Name>REG — Cross-System Asset Registry &amp; Identity Binding (Path A)</Name><OutlineLevel>1</OutlineLevel><Summary>1</Summary><Start>2026-06-01T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish></Task>
<Task><UID>100</UID><ID>100</ID><Name>REG-1 Confirm Path A vs Path B with TEEP IT-security</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-01T08:00:00</Start><Finish>2026-06-01T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Joint working session (Taikun + Darko + Sahir) to confirm IT-security permits sharing per-system asset/well/pad CATALOG data (names, IDs, pad/lease structure, LO contacts) as bootstrap S3 dumps — the trigger for Path A. If IT blocks catalog data sharing, fall back to Path B (TEEP builds GET /v1/assets/{id} resolver, specced in teep-api.yaml §5.2). This is a catalog-metadata-only decision, NOT raw production/SCADA data; frame it that way. Records the decision and which systems (if any) are restricted. Account/tenant IDs are not needed here.</Notes></Task>
<Task><UID>101</UID><ID>101</ID><Name>REG-2 Define per-system catalog dump contract (5 systems)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-02T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Taikun specifies the exact JSON/CSV shape for each system's catalog dump that TEEP places in the bootstrap S3 bucket, matching 04-system-integrations.md §5.1: Sensirion (device_id, pad_id, asset_path, well_ids[], deployed_at, last_seen_ts); Cygnet (asset_id, asset_path, parent, display_name, aliases[]); ProCount (well_id, pad, well_name, lease, operator, api_number); Carte (same as ProCount, may dedup); FMP (pad_id, pad_name, lease, LO contact). Each maps 1:1 to the future REST catalog response so cutover is config-only. Hand to GW-* / bootstrap workstream for S3 wiring.</Notes><PredecessorLink><PredecessorUID>100</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>102</UID><ID>102</ID><Name>REG-3 TEEP produces ProCount well catalog dump (canonical spine)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-03T08:00:00</Start><Finish>2026-06-03T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>IFS Merrick / ProCount owner (TBD) exports the ProCount well list to S3 in the REG-2 shape: well_id, pad, well_name, lease, operator, and CRITICALLY the 14-digit API number per well where present. ProCount is the production-accounting master and becomes the canonical spine, so this dump is built first and is the highest-priority catalog. Confirms API-number availability (drives REG-7 binding strategy A vs C).</Notes><PredecessorLink><PredecessorUID>101</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>103</UID><ID>103</ID><Name>REG-4 TEEP produces Cygnet, Sensirion, Carte, FMP catalog dumps</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-03T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Each TEEP system owner exports its catalog to S3 in the REG-2 shape: Mike → Cygnet asset catalog (asset_id, asset_path, parent, display_name, aliases[]); Michelle/Sebastian → Sensirion device list with device-&gt;pad_id/well_ids/asset_path (this is the same device-&gt;pad/well mapping the SEN workstream pins down — coordinate so it is produced once); ProCount owner → Carte well list (likely identical to ProCount, flag for dedup); Michelle/Sebastian → FMP pad list (pad_id, pad_name, lease, LO contact). Sensirion well_ids-vs-pad_id-only completeness feeds open decision REG-OD3.</Notes><PredecessorLink><PredecessorUID>101</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>104</UID><ID>104</ID><Name>REG-16 Path B fallback — consume TEEP GET /v1/assets/{id} (conditional)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-05T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>ONLY if REG-1 elects Path B (IT blocks catalog data sharing). TEEP builds the cross-system resolver endpoint GET /v1/assets/{id} (specced in teep-api.yaml §5.2: canonical_id, asset_path, aliases by system, wells[], lease_operator). Taikun becomes a thin consumer that calls the endpoint instead of building the registry locally — plug the resolver in front of the same asset_bindings consumption pattern so downstream code is unchanged. This task is dormant unless Path B is elected; effort shown is the Taikun-consumer side (TEEP's endpoint-build effort sits in their workstream).</Notes><PredecessorLink><PredecessorUID>100</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>105</UID><ID>105</ID><Name>REG-5 Build connector-driven binding ingest (Gap 1)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Close gap #1 (07-asset-binding-integration.md §7). Today Data-Catalog Import-Assets writes assets keyed on one external_id but does NOT populate asset_bindings/asset_aliases. Add a per-Atlas-connector binding step: pull catalog -&gt; normalize to BindingRecord {system, external_id, display_name, parent_ref?, api_number?, lat/lon?} -&gt; Resolver.bind(record) routing A) exact key / B) reference / C) fuzzy band. Reuse the existing band+write logic in asset_discovery_service.py (it already writes asset_bindings/asset_aliases/asset_resolution_audit for iSite) — invoke it generically per connector rather than only for iSite. No schema change.</Notes><PredecessorLink><PredecessorUID>101</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>106</UID><ID>106</ID><Name>REG-6 Generalize AssetResolver off system='isite' (Gap 2)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-08T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Close gap #2. AssetResolver binding lookup is pinned to system='isite' (confirmed: _fetch_isite_well_ids in asset_resolver.py queries asset_metadata.asset_bindings WHERE system='isite'). Parameterize to a per-tenant ordered system list (e.g. TEEP: ['procount','cygnet','sensirion','carte','fmp']) so a canonical asset resolves across ALL bound namespaces and reverse-resolves out to each system's native ID. Preserve R2Q behavior (tenant default still 'isite'). The downstream iSite-name DuckDB fallback (_fetch_isite_well_ids_by_name) must also become tenant/source-aware or be skipped for TEEP. No schema change.</Notes></Task>
<Task><UID>107</UID><ID>107</ID><Name>REG-7 Per-system input normalization + number-aware alias merge</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-10T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>From the 2026-05-29 dry-run findings (07 §9.1): (a) add per-system input normalization in the BindingRecord step that strips ' , MV'/' , AA' formation suffixes and '*' markers before matching (pushes the ~19% well review-band toward ~90%+ auto-link); (b) make the alias-merge step reuse the live matcher's number-aware guard so ingest never reproduces the perkins-14-&gt;12 / bare-'12'-&gt;BURNS-SHALLOW-4 collision class. Normalization rules are per-system config, not hardcoded. Include a number-collision unit test ('Bradley Ranch 11' must never bind to 'Bradley Ranch 12').</Notes><PredecessorLink><PredecessorUID>105</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>108</UID><ID>108</ID><Name>REG-8 Build ProCount canonical spine</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-12T08:00:00</Start><Finish>2026-06-12T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Run the connector ingest (REG-5) over the ProCount well dump (REG-3) to create the canonical TEEP-Barnett asset registry: leases, pads, wells with parent_asset_id hierarchy, tenant scoped to TEEP. Bind ProCount well_id via Strategy A (exact API#) where API number present (deterministic conf 1.0), else Strategy C fuzzy on well_name (banded). This is the spine all other systems bind onto. Establish lease-&gt;pad-&gt;well hierarchy so FMP pad-level and Sensirion pad-level bindings can expand to wells.</Notes><PredecessorLink><PredecessorUID>102</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>105</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>107</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>109</UID><ID>109</ID><Name>REG-10 Bind Sensirion onto the spine (reference ONLY, never fuzzy)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-15T08:00:00</Start><Finish>2026-06-15T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Ingest the Sensirion device list (REG-4) and bind by Strategy B (reference) ONLY, via device-&gt;pad_id/well_ids/asset_path. Hard rule: NEVER fuzzy-match Sensirion device IDs (NUB-D-1234 has zero name/API overlap with wells — fuzzy either fails or binds wrong). If a device carries well_ids -&gt; bind to well; else bind to pad and expand via hierarchy. If a device's pad_id/well_ids resolve to no canonical asset, route to review queue — do not fabricate a binding. Coordinate with the SEN workstream: this consumes the same device-&gt;pad/well mapping Sebastian/Michelle deliver. Sensirion is the alert origin (168/168) so a wrong/missing binding breaks every triage fan-out.</Notes><PredecessorLink><PredecessorUID>108</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>103</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>110</UID><ID>110</ID><Name>REG-11 Dedup Carte against ProCount + bind FMP pads</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-15T08:00:00</Start><Finish>2026-06-15T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Carte shares ProCount's IFS Merrick store, so dedup the Carte well list (REG-4) against the ProCount spine — if well_ids match, reuse the existing binding and add a 'carte' binding row pointing to the same canonical asset (likely no separate canonical creation needed). Separately, ingest the FMP pad list (REG-4) and bind pad_id via Strategy B (reference) to the canonical pad, expanding to wells via hierarchy. FMP binding is what targets a TaskHub dispatch to the right pad/LO.</Notes><PredecessorLink><PredecessorUID>108</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>103</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>111</UID><ID>111</ID><Name>REG-9 Bind Cygnet onto the spine (reference/asset_path)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-15T08:00:00</Start><Finish>2026-06-15T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Ingest the Cygnet asset catalog (REG-4) and bind onto the spine. Strategy B (reference) via asset_path where it maps to a canonical pad/well; else Strategy C fuzzy on display_name + Cygnet's aliases[]. Confirm with Mike whether asset_path is a stable reference into the pad/well structure (drives B-vs-C). Cygnet binding is what lets a Sensirion event pull tubing/line/casing pressure for the right asset.</Notes><PredecessorLink><PredecessorUID>108</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>103</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>112</UID><ID>112</ID><Name>REG-12 End-to-end binding validation on the Jan-2026 168-alert set</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-16T08:00:00</Start><Finish>2026-06-16T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Replay the 168 real Jan-2026 alerts (already in emissions.alerts) through the resolver: for each alert's Sensirion device, confirm it resolves to a canonical asset and reverse-resolves out to Cygnet asset_path, ProCount/Carte well_id, and FMP pad_id. Measure fan-out coverage against the per-system mention counts (Cygnet 95/168, FMP 93/168, ProCount 56/168, Carte 22/168) — every mentioned-system alert should reach the named system. Spot-check 20 fan-outs for correct well; confirm zero number collisions across the full TEEP fleet. Gaps feed the review queue / matcher tuning.</Notes><PredecessorLink><PredecessorUID>106</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>111</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>109</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>110</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>113</UID><ID>113</ID><Name>REG-13 Human review-queue triage with TEEP SME</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-18T08:00:00</Start><Finish>2026-06-18T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Work the asset_match_reviews queue (0.60-0.75 band) populated by ingest, using the existing /api/registry/v1/match-reviews UI. A TEEP SME (Devin or an LO who knows the fleet) approves/rejects/relinks ambiguous matches; Taikun applies decisions. This closes the human-in-the-loop and hardens the registry before go-live. Captures approved variants into asset_aliases for future auto-link.</Notes><PredecessorLink><PredecessorUID>112</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>114</UID><ID>114</ID><Name>REG-14 Cutover registry ingest from S3 dumps to live gateway reads</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-18T08:00:00</Start><Finish>2026-06-18T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>When the gateway is live (GW-* dependency), switch the per-system catalog ingest source from bootstrap S3 dumps to live catalog reads through the gateway. Because the dump shapes were defined (REG-2) to match the future REST catalog responses 1:1, this is config-only (point each connector at the gateway endpoint), NOT a rewrite. Re-run ingest and diff against the S3-built registry to confirm parity.</Notes><PredecessorLink><PredecessorUID>112</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>115</UID><ID>115</ID><Name>REG-15 Schedule recurring re-ingest (registry stays current)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-19T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Stand up the recurring re-ingest so the registry tracks fleet changes (new wells, re-deployed Sensirion devices, retired assets). Default cadence = nightly diff (open decision REG-OD4 with Darko; alternative = on-demand per connector sync). Idempotent re-ingest must not duplicate bindings or re-open resolved reviews; new ambiguous matches route to the review queue. Add a freshness/health signal surfaced to the existing Integration Health UI.</Notes><PredecessorLink><PredecessorUID>114</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>116</UID><ID>116</ID><Name>AGENT — Maxwell Close-the-Loop Agent Build</Name><OutlineLevel>1</OutlineLevel><Summary>1</Summary><Start>2026-06-01T08:00:00</Start><Finish>2026-07-24T17:00:00</Finish></Task>
<Task><UID>117</UID><ID>117</ID><Name>AGENT-1 Lock the agent contract: enrichment inputs, TriageResponse, Sierra field mapping</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-01T08:00:00</Start><Finish>2026-06-02T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Joint design session to freeze the internal contracts the workflow depends on: (a) the normalized enrichment-evidence shape each adapter returns (pressure series, codes/comments, injection series, LO notes) independent of whichever upstream pipe delivers it; (b) the TriageResponse schema (reuse the existing emissions_api TriageClassification 6-enum + confidence + recommended_action + classification_rationale JSONB per 03-arch §5.2); (c) the 6-class→3-value resolution_type mapping table (03-arch §0.1); (d) the Sierra column write-map (03-arch §5) and the controlled vocabularies (equipment 7-value, equipment_component, epa_identifier 6-value, resolution_type 3-value). Output is the spec all downstream tasks build against. Assumption: vocabularies come from Sierra (open decision AGENT-OD2), not hardcoded.</Notes></Task>
<Task><UID>118</UID><ID>118</ID><Name>AGENT-2 Add classification_rationale JSONB + status lifecycle states to emissions.alerts</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-03T08:00:00</Start><Finish>2026-06-03T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Migration to add the nullable `classification_rationale` JSONB column (03-arch §5.2) to emissions.alerts and confirm the status enum covers the full lifecycle (Open, enriching, In Review, Closed, Closed (2nd Email)). Add an `enriching` transient status so a crashed agent can resume in-flight events (per 03-arch §6 failure table). No new business columns needed — the 22 Sierra columns already exist (118_emissions.sql). Run against the demo+test DB servers per the migration runbook (PGPASSWORD=taikun psql -h 172.31.44.109).</Notes><PredecessorLink><PredecessorUID>117</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>119</UID><ID>119</ID><Name>AGENT-3 Create emissions.event_audit immutable append-only table</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-03T08:00:00</Start><Finish>2026-06-03T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Apply the event_audit DDL from 03-arch §5.1 (audit_id bigserial, alert_id, emission_id, system, action, endpoint, request_hash, response_hash, status_code, latency_ms, rationale_role, ts). Enforce append-only at the DB layer (no UPDATE/DELETE grants for the app role; revoke + trigger guard). This satisfies FR-9, FR-17, FR-32, NFR-6. Logs are metadata-only (hashes, not raw bodies) per the residency rule.</Notes><PredecessorLink><PredecessorUID>117</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>120</UID><ID>120</ID><Name>AGENT-4 Build the 4 read-enrichment adapters as ActionEngineToolBase tools (against bootstrap)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Implement the teep.* read tools from the 03-arch §4 inventory: teep.cygnet.get_pressure_series + get_compressor_metrics + get_liquids_unloading, teep.procount.get_codes_and_comments + list_work_orders, teep.carte.get_injection_rate, teep.taskhub.get_lo_notes + get_task, teep.sensirion.get_event + list_events. Each wraps a shared TeepAdapter seam whose data source is config (signed-S3-JSON bootstrap or Prism mock now; live gateway later — identical JSON shape so cutover is config-only). Each tool normalizes to the AGENT-1 EnrichmentEvidence schema, writes an event_audit row, returns ToolResult. NOTE: the Cygnet tools' upstream input shape is provisional until the SCADA-via-existing intermediary path is chosen (depends on CYG-* / Mike+Darko) — code to the bootstrap shape and adapt at cutover.</Notes><PredecessorLink><PredecessorUID>117</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>119</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>121</UID><ID>121</ID><Name>AGENT-5 Build ai.parallel_fetch enrichment fan-out stage tool</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-10T08:00:00</Start><Finish>2026-06-11T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Implement the enrich_parallel stage (03-arch §0.2) that fans out the 4 read adapters concurrently (asyncio.gather) over the FR-5..FR-8 time windows ([start-4h,start+1h] for Cygnet/ProCount/Carte; [start-2h,start+4h] for TaskHub), resolving the affected asset to a canonical asset_id via AssetResolver FIRST (FR-36, never raw user text to SQL). Per-system failure is isolated: a down system yields a 'no_match'/unavailable evidence marker (never aborts the fan-out) and is logged to event_audit — implements the FR-5..FR-8 + §6 failure semantics. Returns the merged EnrichmentEvidence bundle to the workflow.</Notes><PredecessorLink><PredecessorUID>120</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>122</UID><ID>122</ID><Name>AGENT-6 Implement rule-cascade pre-classifier from the 14 how_cleared templates</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-12T08:00:00</Start><Finish>2026-06-16T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Implement the deterministic rule cascade (03-arch §8 pseudocode) as a stage tool: full multi-system signature (Cygnet pressure + ProCount down-code + Carte injection drop → Process Emissions 0.94); pressure + LO-note (76-event dominant pattern → 0.88); compressor-specific (0.90); liquids-unloading (0.92); sensor-cleared-quickly/no-signal false-positive (0.72); no-system-match + sustained sensor → Unexpected 0.81 requires_dispatch; default insufficient-evidence → Undetected 0.55. Emits a partial TriageResponse + a rationale_role for audit. Thresholds are config-driven (calibrated on 22 days, retuned in AGENT-15), never hardcoded. Sets confidence so the LLM-fallback gate (AGENT-7) fires only when needed.</Notes><PredecessorLink><PredecessorUID>121</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>117</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>123</UID><ID>123</ID><Name>AGENT-7 Extend Maxwell context-injection LLM triage to consume live enrichment (LLM fallback)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-17T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Extend the existing _build_alert_context / Maxwell prompt (emissions_api.py) to inject the live EnrichmentEvidence (Cygnet pressure drops, ProCount comp_down codes, Carte injection, TaskHub LO free-text) alongside the historical context it already builds (FR-11, 03-arch §4.1). Wire it as the ai.maxwell.triage stage tool, invoked ONLY when rule confidence &lt; 0.70 AND TaskHub LO free-text exists. Output the full TriageResponse (classification + confidence + recommended_action + structured classification_rationale citing specific evidence, FR-15). All LLM calls route through the LLM gateway (taikun_llm / Bedrock per the Bedrock workstream) with audit_context → platform.llm_calls. Phase-1 pattern is context-injection (single call, predictable cost), not agentic (03-arch §4.2).</Notes><PredecessorLink><PredecessorUID>122</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>124</UID><ID>124</ID><Name>AGENT-15 Calibrate rule-cascade thresholds + LLM accuracy against historical data</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-22T08:00:00</Start><Finish>2026-06-25T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Re-tune the rule-cascade confidence thresholds and pressure/injection-drop magnitudes (currently 22-day-calibrated, 03-arch §8 note) against the 6-12 month historical pull (depends on Q5 from Sierra/Clovis). Build a labeled-set harness from the historical resolutions, measure classification accuracy vs the &gt;=92% Phase-1 KPI, tune thresholds, and validate the 6→3 mapping holds across seasons. Quantify the realistic auto-close rate vs the 40% target and the 56.5% office-cleared ceiling. Honest dependency: if the historical pull slips, this slips and we cannot stand behind the accuracy KPI at go-live — flag to Clovis.</Notes><PredecessorLink><PredecessorUID>123</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>122</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>125</UID><ID>125</ID><Name>AGENT-8 Implement the decide/branch + 6→3 resolution_type mapping stage</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-22T08:00:00</Start><Finish>2026-06-22T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Implement the branch_action stage (03-arch §0.2): map TriageResponse → one of auto_close / open_dispatch / escalate_mro per the §0.1 table (process_emission|false_alarm→office_resolve→Process Emissions; real_leak|thief_hatch|equipment_issue→dispatch_crew→Unexpected; needs_inspection→investigate→Undetected). Apply the confidence gates (FR-12 &lt;0.65→Undetected→MRO; FR-20 auto-close requires &gt;=0.85). Pre-populate provisional cleared_location/equipment/equipment_component/resolution_type and write the reasoning fields (problem_identified, classification_rationale, resolution_personnel='Maxwell AI') via emissions.alerts.update. This is the deterministic router between the three act paths.</Notes><PredecessorLink><PredecessorUID>123</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>118</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>126</UID><ID>126</ID><Name>AGENT-10 Dispatch path: idempotent TaskHub create_task with evidence pack</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-23T08:00:00</Start><Finish>2026-06-25T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Implement teep.taskhub.create_task (POST /v1/fmp/tasks, 03-arch §3.2 / teep-api.yaml): body = full evidence pack (kg/h, Maxwell rationale, linked emission_id, pad/well, similar prior events) + callback_webhook={taikun}/taskhub/events. Idempotency-Key header (client-supplied request id) for 24h replay safety; OAuth2 write-scope separate from read scope; rate-limited &lt;=5/min; RFC7807 problem+json error handling; audited to event_audit. On 201, UPDATE alert cleared_location='Field', status='Open' (awaiting LO). Bootstrap fallback (02-prd §8.1): if TaskHub write isn't live by week 4, config-toggle to email the MRO evidence pack instead of POSTing — same evidence, different sink.</Notes><PredecessorLink><PredecessorUID>125</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>127</UID><ID>127</ID><Name>AGENT-11 Escalate path: surface In Review in MRO advisor queue with full decision trace</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-23T08:00:00</Start><Finish>2026-06-23T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Implement the escalate_mro path (FR-22, 03-arch §3.3): UPDATE status='In Review', surface the alert in the existing /advisor/queue with the full decision trace (every enrichment call, the rule-cascade result, the LLM rationale, the event_audit rows) queryable per FR-33 ('show every API call and state change for alert #168'). Wire notify.mro_advisor. Triggered by needs_inspection or confidence&lt;0.65. This is the never-silently-drop backstop (NFR-5).</Notes><PredecessorLink><PredecessorUID>125</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>119</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>128</UID><ID>128</ID><Name>AGENT-9 Auto-close path: write final Sierra fields + closeout email + auto-close gate</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-23T08:00:00</Start><Finish>2026-06-24T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Implement emissions.alerts.close for the office-resolve path (FR-20, 03-arch §3.1): write how_cleared (standardized template matching real Jan-2026 text per evidence pattern), resolution_date=NOW, cleared_location='Office', resolution='sent email to close out alert', status='Closed', and trigger notify.smtp_closeout (Sierra's standard closeout distro). Gate behind a 3-state per-environment auto-close control — shadow (write nothing, log intended action), human-confirm (queue for Devin one-click approve), autonomous (no human touch) — defaulting to shadow at go-live (open decision AGENT-OD1). how_cleared templates are config/data, not hardcoded.</Notes><PredecessorLink><PredecessorUID>125</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>129</UID><ID>129</ID><Name>AGENT-12 Monitor + 24h-timeout: durable wait on TaskHub webhook + Sensirion return-to-baseline</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-26T08:00:00</Start><Finish>2026-07-01T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Implement the monitor_dispatch stage (FR-23/24, 03-arch §3.2/§3.3): subscribe to the inbound TaskHub task.updated webhook (HMAC X-TEEP-Signature verify, +/-5min skew, replay-protected) AND poll teep.sensirion.get_event every 5 min for return-to-baseline; optional PATCH (teep.taskhub.update_task) for intermediate agent notes if sensor stays elevated. Close-out fires when LO marks task done AND sensor &lt; threshold, OR 24h timeout. MUST run on the durable substrate (DBOS) — a dispatched event can sit open up to 24h and the timer must survive agent restarts/redeploys (03-arch §6: in-flight retried from status='enriching'). This is the one part of the workflow explicitly NOT exempt from durable execution.</Notes><PredecessorLink><PredecessorUID>126</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>130</UID><ID>130</ID><Name>AGENT-13 Close-the-loop finalize: read LO findings, map to Sierra cols, PATCH TaskHub closed</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-07-02T08:00:00</Start><Finish>2026-07-06T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Implement the finalize stage (FR-24, 03-arch §3.2): on monitor completion, GET the final TaskHub task (teep.taskhub.get_task), map LO findings to Sierra's columns (problem_identified ← LO note; equipment; equipment_component; thief_hatches_open/_repaired/_replaced), set how_cleared='The alerts was cleared with a visit to the field.', resolution_date=NOW, resolution_personnel=LO name, status='Closed' via emissions.alerts.close; then idempotent PATCH /v1/fmp/tasks/{id} status=closed (teep.taskhub.close_task) ONLY if the LO hasn't already closed it (03-arch §3.2 alt branch — never double-close). On 24h-timeout instead: PATCH an escalation note + route to AGENT-11 escalate path (never auto-close a stuck event).</Notes><PredecessorLink><PredecessorUID>129</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>128</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>131</UID><ID>131</ID><Name>AGENT-14 Compose + register the emissions_triage_close_loop JSON workflow (durable, gateway-routed)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-07-07T08:00:00</Start><Finish>2026-07-09T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Assemble all stage tools into the emissions_triage_close_loop JSON workflow (03-arch §0.2 shape, modeled on tank_overflow_protection_with_servicenow + ring_energy_ai_traffic_cop_v3's interlock/timeout pattern) under workflows/templates/. Trigger = Sensirion webhook (sensirion.kg_per_hr_threshold_crossed). Steps: open_alert → enrich_parallel → maxwell_triage(rule+LLM) → branch_action → {auto_close | open_dispatch→monitor_dispatch→finalize | escalate_mro}. Implement open_alert (emissions.alerts.insert, FR-16: status=Open, dedupe by device+location+open-window FR-4) and the Sensirion webhook receiver (HMAC verify). Runs through ModularWorkflowDispatcher→WorkflowEngine + LLM gateway; durable substrate enabled (stateful/long-running); editable in ReactFlow; observable in run history. Per-stage failure → audit + never silently drop (NFR-5: log Undetected + notify within 5 min).</Notes><PredecessorLink><PredecessorUID>128</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>126</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>127</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>130</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>132</UID><ID>132</ID><Name>AGENT-16 Drive the full agent through the 4-scenario simulator on the test VM</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-07-10T08:00:00</Start><Finish>2026-07-14T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Run the existing 4-scenario agent simulator (auto-close / dispatch / monitor / timeout) end-to-end against the assembled emissions_triage_close_loop workflow on the AWS test VM, exercising the Prism-mocked teep-api.yaml. Verify each scenario produces correct alert state, correct TaskHub POST/PATCH (or email fallback), correct audit trail, and correct timeout escalation — all per VM verification policy (no local testing). Confirm idempotency (replayed webhooks/keys don't double-act) and failure isolation (each system forced down in turn still yields a non-dropped event).</Notes><PredecessorLink><PredecessorUID>131</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>133</UID><ID>133</ID><Name>AGENT-19 Agent observability: triage metrics + per-system API health + failure alerting</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-07-10T08:00:00</Start><Finish>2026-07-13T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Emit the FR-35 metrics (events-per-day, classification-mix, MTTA, MTTR, auto-close-rate, dispatch-to-close time, per-system API-error-rate, TaskHub open→close round-trip) and the 03-arch §7 Prometheus→CloudWatch series. Implement FR-34 failure alerting: &gt;=5 failures within 5 min for the same system → notify the Darko-designated contact within 5 min (NFR-5, never silently drop). Surface on the existing Triage Live + Audit dashboards. Confirms the agent's behavior is observable for the pilot KPI scorecard.</Notes><PredecessorLink><PredecessorUID>131</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>134</UID><ID>134</ID><Name>AGENT-17 Cut adapters over from bootstrap/mock to live TEEP gateway (config-only)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-07-14T08:00:00</Start><Finish>2026-07-16T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Flip the TeepAdapter seam from signed-S3/Prism to the live TEEP gateway endpoints (config change, not code — the JSON shapes are identical by design). Adapt the Cygnet adapter to whatever intermediary path was chosen (FMP ~30-min poll / ProCount Cygnet integration / historian export — from CYG-*/Mike+Darko). Wire live OAuth2 client-credentials (read scope + separate write scope), live Idempotency-Key store, live HMAC webhook secrets, live LLM gateway→Bedrock. Re-run the 4-scenario simulator against live endpoints. Honest dependency: this gates on GW (gateway live), Sensirion API (SEN-*), TaskHub API (Sebastian/FMP), and Bedrock (BED-*) all being ready — these are the real schedule risks that push beyond the deck's 8 weeks.</Notes><PredecessorLink><PredecessorUID>132</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>135</UID><ID>135</ID><Name>AGENT-18 Shadow-mode pilot on live traffic, then graduate the auto-close gate</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-07-17T08:00:00</Start><Finish>2026-07-24T17:00:00</Finish><Duration>PT48H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Run the agent in shadow mode against live Sensirion traffic (writes intended actions to event_audit + a shadow column, sends nothing, dispatches nothing) for ~1-2 weeks; compare the agent's intended classification/action against what the MRO team actually does. Devin reviews divergences. Then graduate the auto-close gate: shadow → human-confirm (Devin one-click approves auto-closes and dispatches) → autonomous for Process Emissions only. Dispatch and escalate paths can go live earlier than autonomous auto-close (lower blast radius). This is the safe on-ramp the customer needs before fully-autonomous closure (AGENT-OD1).</Notes><PredecessorLink><PredecessorUID>134</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>128</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>136</UID><ID>136</ID><Name>REPORT — Reporting &amp; UI — Sierra HSE/EPA Export + 4 Screens</Name><OutlineLevel>1</OutlineLevel><Summary>1</Summary><Start>2026-06-01T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish></Task>
<Task><UID>137</UID><ID>137</ID><Name>REPORT-1 Confirm Sierra's exact live HSE/EPA column list, sheet scope, and vocabularies</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-01T08:00:00</Start><Finish>2026-06-01T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Working session with Sierra to lock the authoritative spec for the export. Resolve the three known ambiguities: (a) which alert sheet is her live template — 'Sensirion Alert Data' (Resolution Personnel / Problem Identified) vs ' Sensirion Data' (MRO Resolution Personnel: / Problem Identified via Email reply:); (b) whether the monthly file is one combined workbook with all 5 sheets (alerts, daily-notes, linked-notes, pad-baselines, pads-without-alerts) or just the alerts sheet; (c) controlled-vocab rulings on the dirty values found in the real xlsx — 'Process Emissions ' trailing-space dupes, the stray blank in Resolution Type:, and the use of literal 'Process Emissions' as an Equipment / Equipment Component / EPA identifier value. Capture exact header strings verbatim including trailing spaces and punctuation. This is the PRD Q4 open item; confirm against a current (not Jan) copy of her file in case headers drifted. Assumption if Sierra unavailable: default to sierra-xlsx-analysis.md 'Sensirion Alert Data' 22-column set, alerts-sheet-only, with canonicalized vocabularies — recorded as the recommended default.</Notes></Task>
<Task><UID>138</UID><ID>138</ID><Name>REPORT-2 Build config-driven Sierra header-map + vocabulary canonicalization layer</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-02T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Implement the column spec from REPORT-1 as runtime configuration consumed by the export tool (no hardcoded headers in Python — per the No-Hardcoding rule). Two parts: (1) a header-map (emissions.alerts field -&gt; Sierra verbose header, with explicit ordering); (2) a canonicalization function that maps stored values to the controlled set (trim whitespace, collapse 'Process Emissions ' -&gt; 'Process Emissions', map empty Resolution Type to a defined default). The canonicalizer must be importable so Track A (ACT) writers use the SAME normalization at write-time — agree the shared module boundary with ACT so emissions.alerts is clean by construction and the export is clean by SELECT. Cover resolution_type (3), equipment (7 per FR-13), equipment_component (10), epa_identifier (6 per FR-14).</Notes><PredecessorLink><PredecessorUID>137</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>139</UID><ID>139</ID><Name>REPORT-11 Build Integration Health screen</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build the Integration Health screen (mockup 04): per-system cards (Sensirion, Cygnet, TaskHub, ProCount, Carte) showing UP/DEGRADED, read/write capability, 24h call count, success %, p95 latency, last error; API-call-volume chart; recent-failures list; and the notification policy line (&gt;=5 failures in 5 min -&gt; email designated contact, per FR-34). All numbers come from emissions.event_audit (system, endpoint, status_code, latency_ms) and the observability metrics in 03-architecture §7. IMPORTANT: real success/p95/last-error numbers only become meaningful once the gateway + per-system adapters are emitting to event_audit — so ship first against bootstrap/audit data (or the agent-simulator's runs) and get real per-system numbers at gateway cutover. The 'designated notification contact' is an open decision (PRD Q7) — default Darko's email, confirmed by Darko.</Notes></Task>
<Task><UID>140</UID><ID>140</ID><Name>REPORT-3 Sierra-format Excel export endpoint + tool (emissions.alerts.export_sierra_format)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-05T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Net-new server-side export: an ActionEngineToolBase tool (e.g. emissions.report.export_sierra) plus GET /api/emissions/export?format=xlsx endpoint (no such endpoint exists today in emissions_api.py — confirmed). Renders emissions.alerts to a workbook using openpyxl (already transitively available — used in forge/service.py and reserve_economics) with Sierra's exact ordered verbose headers from REPORT-2, controlled-vocab values canonicalized, and the sheet scope decided in REPORT-1. Applies the filter parameters from REPORT-6 (date range, resolution_type, route, equipment, equipment_component, cleared_location). Satisfies FR-28/FR-30 (Excel matches her template column-for-column). Output must be byte-comparable in structure to her existing file so it is a true drop-in.</Notes><PredecessorLink><PredecessorUID>138</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>141</UID><ID>141</ID><Name>REPORT-6 Reporting filter bar (date range, resolution_type, route, equipment, equipment_component, cleared_location)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Implement FR-29 filters as a thin Tabler filter bar that builds query params for the existing /api/emissions/alerts endpoint and the export endpoints. Filter values (routes, equipment, etc.) are fetched from the data, not hardcoded — there are 16 real routes and the controlled vocabularies from REPORT-1. Filter state must flow into both the on-screen table and the Excel/PDF export so 'what you see is what you export'.</Notes><PredecessorLink><PredecessorUID>138</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>142</UID><ID>142</ID><Name>REPORT-8 Sierra override-any-field UI + audited PATCH endpoint</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Implement FR-31: Sierra can correct any auto-populated field from the UI, and the override is audited append-only to emissions.event_audit (FR-32, NFR-6). Build a per-event edit affordance on the Event Detail / reporting row that PATCHes emissions.alerts and writes an event_audit row (system='agent'/'ui', action='override', before/after value hashes, the editing user, timestamp). Override-eligible fields come from REPORT-1's per-column flag. CRITICAL DEPENDENCY: true attribution of 'which TEEP user made the change' requires Entra ID/SSO claims (SSO workstream, SSO-*). Until SSO lands, record overrides under a configured pilot identity and flag them as unattributed — do not block the feature, but the audit is not regulator-grade until SSO is wired. The event_audit append-only guarantee is shared with ACT — coordinate the table contract.</Notes><PredecessorLink><PredecessorUID>137</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>143</UID><ID>143</ID><Name>REPORT-9 Finalize Triage Live screen (MRO command center)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-04T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Finalize the Triage Live screen for the MRO team (Devin et al.) — the live advisor tab already exists (mockup 02, the Emissions Advisor tab in emissions.html backed by /advisor/queue + /advisor/insights). Finalize: queue ranked by severity/kg-h, classification mix today, MTTA-last-24h, and per-row action chips (Auto-closed / Dispatched / In Review) consistent with the three-path model (~70% auto / ~27% dispatch / ~3% escalate). Add the 'live · auto-refresh' behavior. This is finalize-not-build; the data endpoints exist today.</Notes></Task>
<Task><UID>144</UID><ID>144</ID><Name>REPORT-10 Build Event Detail + Maxwell decision-trace screen</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-08T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build the per-event detail screen (mockup 03): header (emission_id, pad, kg/h, sensor status, resolution_type), the Maxwell reasoning panel (problem_identified + classification_rationale JSONB rendered as cited evidence), the Live API enrichment panel (the 4-system reads with match/no-match status), and the full decision trace from emissions.event_audit satisfying FR-33 ('show me every API call and every state change the agent made for alert #168, including the TaskHub task it created and the LO note it consumed'). Render classification_rationale.evidence[] (system/endpoint/finding) and similar_prior_events. The override affordance from REPORT-8 lives here. Frontend thin — all trace data comes from /advisor/triage and an event_audit read endpoint.</Notes><PredecessorLink><PredecessorUID>142</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>145</UID><ID>145</ID><Name>REPORT-7 Live update of reporting view as Maxwell closes events</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-05T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>PRD Track B requires 'live updates as events close'. Add lightweight polling refresh (the existing UI already has an auto-refresh affordance — 'live · auto-refresh' badge in mockup 01) so the reporting KPIs and table re-fetch when Maxwell writes a close (status-&gt;Closed) to emissions.alerts. No websockets needed for Phase 1 (~7.6 events/day); a 30-60s poll of /summary + /alerts is sufficient. Show a subtle 'updated Xs ago' indicator.</Notes><PredecessorLink><PredecessorUID>141</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>146</UID><ID>146</ID><Name>REPORT-13 Optional combined-workbook export (daily notes, linked notes, pad baselines, pads-without-alerts)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-08T08:00:00</Start><Finish>2026-06-08T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>If REPORT-1 determines Sierra's deliverable is the full multi-sheet workbook (not just the alerts sheet), extend the export to emit all 5 sheets from emissions.* — Sensirion Alert Data (alerts), daily-notes (1,677 rows), Relevant LO Notes (linked_notes), Sensirion Data baseline, and Pad List of Dates wo Alerts (pad_baselines). Each sheet uses its own verbose-header map from sierra-xlsx-analysis.md. Scoped as conditional/optional because it only applies if REPORT-1 confirms multi-sheet scope; otherwise drop.</Notes><PredecessorLink><PredecessorUID>137</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>140</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>147</UID><ID>147</ID><Name>REPORT-4 PDF export of the monthly HSE/EPA report</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-08T08:00:00</Start><Finish>2026-06-08T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Add format=pdf to the export endpoint (FR-30: report exportable as Excel AND PDF). PDF is a presentation rendering, not a column-for-column file — a paginated table of the same filtered emissions.alerts rows plus the KPI header (total events, resolution-type mix, office-cleared %, thief-hatch summary) matching the Overview screen. Use a server-side renderer consistent with existing platform PDF generation (reserve_economics report_generator_service.py already does PDF — reuse that approach rather than introduce a new dependency). Honors the same filters as the xlsx export.</Notes><PredecessorLink><PredecessorUID>140</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>148</UID><ID>148</ID><Name>REPORT-5 Wire the Reporting Monthly Report screen into emissions.html (replace generic Export)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Replace the current generic advisor 'Export' button with a proper Monthly Report view. The Overview tab (mockup 01) already exists; add the Sierra one-click 'Export Excel (HSE format)' and 'Export PDF' buttons (shown in mockup 01 top-right) wired to REPORT-3/REPORT-4 with the active filter state. Frontend stays thin — vanilla JS glue only, Tabler classes only, no domain logic in the browser (the column mapping and vocab live server-side). Trigger a download; show a spinner + error toast if the server fails (do not mask backend failures client-side).</Notes><PredecessorLink><PredecessorUID>140</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>147</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>141</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>149</UID><ID>149</ID><Name>REPORT-12 Sierra acceptance: side-by-side export validation against her real file</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-19T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Validation gate with Sierra: generate the monthly export for a real month and diff it against her hand-keyed file for the same period — header strings, column order, controlled-vocab values, row count, and a spot-check of populated values. Confirm it is a true drop-in (she would file the generated file unchanged). Capture any header/vocab corrections and feed them back as config edits (no code change, by design). This is the Track-B acceptance milestone tied to PRD success criterion #4 (monthly report generated entirely from the agent's data store, no Outlook transcription).</Notes><PredecessorLink><PredecessorUID>148</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>145</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>138</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>150</UID><ID>150</ID><Name>DATA — Data Baseline, KPIs &amp; Pilot Success Criteria</Name><OutlineLevel>1</OutlineLevel><Summary>1</Summary><Start>2026-06-01T08:00:00</Start><Finish>2026-06-30T17:00:00</Finish></Task>
<Task><UID>151</UID><ID>151</ID><Name>DATA-1 Issue and scope the 6-12 month historical data request to TEEP</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-01T08:00:00</Start><Finish>2026-06-01T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Convert PRD open-item Q5 into a concrete, signed data request. Define exactly what is needed per system for KPI firming + labeling: full Sensirion alert export (the Sierra-xlsx shape: emission_id, pad, pad_code, route, emission_rate_kgh, emission_start, email_received, cleared_location, resolution_date, resolution_personnel, problem_identified, how_cleared, resolution_type, equipment, equipment_component, epa_identifier, thief_hatch counts) for the longest window available (target 12 months, minimum 6); the linked LO notes and daily-notes for the same window; and a statement of what kg/hr-threshold-crossing timestamp (vs. email_received) Sensirion can provide historically. Specify delivery as signed S3 JSON drops in the same shape as the future REST responses (per the bootstrap pattern) so ingestion code is cutover-ready. Owner contact is Sierra (data) + Clovis (approval). ASSUMPTION: TEEP can export beyond Jan 2026; if Sensirion only retains a shorter window, that becomes a hard KPI-confidence limit recorded as a risk.</Notes></Task>
<Task><UID>152</UID><ID>152</ID><Name>DATA-2 Receive &amp; ingest the historical pull into emissions.* (extend ingestion)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-04T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Take delivery of the historical S3 JSON drops and ingest them into emissions.alerts / daily_notes / linked_notes / pad_baselines, extending the existing scripts/ingest_emissions_xlsx.py loader to handle a multi-month, JSON-shaped feed (not just the single Jan xlsx) and to be idempotent on emission_id. Validate row counts, date coverage, controlled-vocab conformance (resolution_type 3 values, equipment 7, equipment_component 10, epa_identifier 6 — normalizing the trailing-space dupes seen in the Jan sample like 'Process Emissions ' x2), and null rates on KPI-critical columns. Quarantine and report malformed rows; never silently drop (per Fail-and-Fix rule).</Notes><PredecessorLink><PredecessorUID>151</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>153</UID><ID>153</ID><Name>DATA-4 Add emissions.event_audit + KPI instrumentation columns to the schema</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-03T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Build the missing instrumentation substrate. Create emissions.event_audit (append-only: alert_id, system, endpoint, request/response hash, status_code, latency_ms, rationale_role, ts) exactly as FR-17/FR-32 require — it does not exist in schema/118_emissions.sql today. Add KPI-bearing columns to emissions.alerts that the current schema lacks: acknowledged_at (classified timestamp for MTTA), classification_confidence numeric, auto_closed boolean, dispatch_task_id text, dispatched_at / field_closed_at (for dispatch-to-close MTTR), classification_rationale jsonb (FR-15), and resolution_personnel already supports 'Maxwell AI'. Ship as a numbered migration. Coordinate timestamps with the Agent/Action workstream so the agent actually writes these fields. Run against the test DB first.</Notes></Task>
<Task><UID>154</UID><ID>154</ID><Name>DATA-3 Recompute KPI baselines on the full history &amp; seasonality analysis</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Re-derive every baseline currently quoted from the 22-day sample against the full historical pull: median MTTA proxy (email_received - emission_start), median resolution time (resolution_date - email_received), % office vs. field cleared (Jan: 56.5% office), resolution-type mix (Jan: 70.2/27.4/2.4), daily volume (Jan: ~7.6/day, ~230/mo), median emission rate (Jan: 21.4 kg/h). Break out by month/season to test whether the January distribution is representative (the core Q5 question). Produce updated, defensible Phase-1 target ranges where the Jan single-point numbers no longer hold. Explicitly separate the email-delay MTTA proxy from any true kg/hr-threshold MTTA (see DATA-9).</Notes><PredecessorLink><PredecessorUID>152</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>155</UID><ID>155</ID><Name>DATA-5 Build the KPI metrics layer &amp; pilot scorecard endpoint</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-10T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Implement the agent-performance KPIs that PRD FR-35 lists but that are not computed today: MTTA (acknowledged_at - emission_start, split by P1/P2 thresholds), MTTR / dispatch-to-close (field_closed_at - dispatched_at), auto-close rate (auto_closed=true / total), classification-mix, per-system API error rate (from event_audit status_code), and TaskHub round-trip latency (open-&gt;close). Extend the existing KpiSummary model + GET /api/emissions/summary rather than inventing a parallel ontology, and add a dedicated pilot-scorecard endpoint that renders the PRD §11 success criteria as live pass/fail tiles. Surface in the existing emissions.html reporting tab (thin frontend, no client-side logic). NO hardcoded numbers — all from queries over alerts + event_audit.</Notes><PredecessorLink><PredecessorUID>153</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>156</UID><ID>156</ID><Name>DATA-7 Define the labeled validation set &amp; freeze it</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-08T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Design a stratified, frozen validation set carved from the historical pull for measuring classification accuracy. Preserve the real class imbalance but over-sample the rare classes so they are measurable: Undetected was only 4/168 in Jan, and field-cleared (~43.5%) and the long-tail equipment/epa_identifier vocab values are sparse. Target ~300-400 labeled events spanning multiple months/seasons. Define the label schema (ground-truth resolution_type + equipment + equipment_component + epa_identifier + cleared_location + a binary 'should-have-auto-closed') and store it in a new emissions.alert_labels table keyed by emission_id. Critically: this set is HELD OUT of all prompt/rule tuning to prevent leakage. Document sampling method and rationale.</Notes><PredecessorLink><PredecessorUID>152</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>157</UID><ID>157</ID><Name>DATA-9 Reconstruct the true MTTA baseline (kg/hr threshold, not email proxy)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-08T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Resolve the most important measurement gap: the published 5.2h baseline is the email-delay proxy (email_received - emission_start), but the PRD KPI G1/§11 target is measured 'from kg/hr threshold crossing'. Sierra's xlsx has no threshold-crossing timestamp. Work with the Sensirion-API workstream to obtain, from Sensirion device data, the actual time the kg/hr threshold (FR-3, default 1 kg/hr) was crossed for the historical events, and recompute the true baseline MTTA. If Sensirion cannot supply historical threshold timestamps, document that the baseline remains an email-delay proxy and that the post-Maxwell MTTA (webhook-driven) is measured on a different clock — a comparability caveat that must be stated in the success criteria.</Notes><PredecessorLink><PredecessorUID>152</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>158</UID><ID>158</ID><Name>DATA-8 Human ground-truth labeling pass (Devin + Sierra adjudication)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-12T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Run the actual labeling. Devin (MRO triage) and Sierra (HSE reporting) adjudicate the held-out validation set: confirm or correct resolution_type / equipment / equipment_component / epa_identifier / cleared_location and mark whether each event should have been auto-closed from the office. Provide a lightweight labeling view (reuse the Event Detail screen) so they label against the same evidence the agent sees. Capture inter-annotator agreement on a 30-event overlap to quantify label noise (this caps the achievable accuracy — if humans only agree 94%, a 92% target is reasonable; if they agree 99%, push higher). Resolve disagreements by discussion, log final labels to emissions.alert_labels.</Notes><PredecessorLink><PredecessorUID>156</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>159</UID><ID>159</ID><Name>DATA-11 Lock the 60-day pilot success criteria &amp; measurement plan</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-10T08:00:00</Start><Finish>2026-06-10T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Convert PRD §11's six criteria into a signed, measurable acceptance contract: (1) &gt;=95% events ingested without human intervention; (2) median MTTA &lt;=15 min from kg/hr threshold (subject to DATA-9's clock decision); (3) &gt;=40% auto-closed as Process Emissions; (4) Sierra's monthly report generated entirely from the agent store (no Outlook transcription); (5) no API incident requiring Darko's team to roll back access; (6) Devin and Sierra confirm in writing they would not return to manual. For each, define the exact metric source (which query/endpoint), the measurement window (60 days production), and the threshold. Bind each to the DATA-5 scorecard tiles. Get Clovis + Darko + Sierra + Devin sign-off.</Notes><PredecessorLink><PredecessorUID>154</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>155</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>157</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>160</UID><ID>160</ID><Name>DATA-6 Define &amp; build the methane-avoided estimator (instrumented, not a static deck number)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-10T08:00:00</Start><Finish>2026-06-11T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Turn the deck's methane-avoided figure (~70 t CH4 over the 22-day sample; ~33,000 t CO2e/yr at GWP 28 IPCC AR5) into a reproducible, query-driven estimate. Method per the deck: per-event vented mass = median(or per-event) emission_rate_kgh x leak_duration, where leak_duration = MTTA + resolution time; avoided = baseline_duration_vented - maxwell_duration_vented; aggregate across events and annualize from the run-rate. Parameterize GWP (28, AR5) and the duration model as config, not constants. Document every assumption (rate is sensor-driven and unchanged; only duration shrinks) so the number survives HSE/EPA scrutiny. Validate the estimator reproduces the deck's ~70 t on the Jan sample, then re-run on full history.</Notes><PredecessorLink><PredecessorUID>154</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>155</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>161</UID><ID>161</ID><Name>DATA-10 Measure classification accuracy vs. the labeled set</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-15T08:00:00</Start><Finish>2026-06-17T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Run the live classifier (rule cascade + Maxwell LLM fallback per FR-11/FR-12) over the held-out validation set and produce the accuracy report: overall accuracy, per-class precision/recall/F1 (Process Emissions / Unexpected / Undetected), full confusion matrix, and the abstention/escalation behavior (confidence &lt;0.65 -&gt; Undetected -&gt; MRO review — verify it abstains rather than guesses). Report accuracy both raw and adjusted for the inter-annotator ceiling from DATA-8. Compare against the PRD &gt;=92% Phase-1 target. Feed any systematic error modes back to the Agent workstream as tuning items WITHOUT touching the held-out set.</Notes><PredecessorLink><PredecessorUID>158</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>162</UID><ID>162</ID><Name>DATA-12 Operate the live pilot scorecard during the 60-day window</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-19T08:00:00</Start><Finish>2026-06-26T17:00:00</Finish><Duration>PT48H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Once Maxwell is live on TEEP APIs (post-cutover), run the scorecard continuously over the 60-day production window. Weekly KPI review with Clovis/Devin/Sierra; track MTTA, MTTR, auto-close rate, ingestion completeness, per-system API error rate, and a rolling shadow-accuracy check (sample of live closures spot-checked against MRO/LO ground truth) to detect drift from the historical accuracy. Maintain the free-text -&gt; category mapping table reviewed by Sierra weekly (per the PRD risk row). Flag any criterion trending to miss early enough to act.</Notes><PredecessorLink><PredecessorUID>155</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>159</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>163</UID><ID>163</ID><Name>DATA-13 Run the Phase-1 -&gt; Phase-2 go/no-go review</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-29T08:00:00</Start><Finish>2026-06-30T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>At the end of the 60-day window, produce the go/no-go decision package measured against the DATA-11 signed criteria: each of the 6 criteria pass/fail with evidence, the measured-vs-target table for every PRD §7 KPI, the realized methane-avoided figure (DATA-6 estimator on live data), the final classification accuracy on live traffic, and a recommendation on Phase-2 targets (auto-close 40%-&gt;65%, accuracy 92%-&gt;95%, MTTA &lt;5min). Capture the written confirmations from Devin and Sierra (criterion 6). Present to Clovis + Darko for the formal decision.</Notes><PredecessorLink><PredecessorUID>162</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>161</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>160</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>164</UID><ID>164</ID><Name>CUTOVER — Integration Testing, Cutover, Go-Live and Operate</Name><OutlineLevel>1</OutlineLevel><Summary>1</Summary><Start>2026-06-01T08:00:00</Start><Finish>2026-07-06T17:00:00</Finish></Task>
<Task><UID>165</UID><ID>165</ID><Name>CUTOVER-1 Cutover readiness kickoff + per-system go-live sequencing plan</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-01T08:00:00</Start><Finish>2026-06-01T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Run a joint kickoff to agree the per-system go-live order (read systems first: Sensirion, Cygnet, ProCount, Carte; TaskHub write last), the base-URL cutover mechanism (config-only flip from bootstrap S3-drop source to gateway), the go/no-go gates per system, and the email-fallback trigger if TaskHub write slips. Produce a one-page sequencing plan + RACI naming the go-live owner per slice (Michelle/Sebastian for Sensirion+TaskHub, Mike for Cygnet, IFS Merrick owner for ProCount/Carte, Darko for gateway, Sahir for SSO).</Notes></Task>
<Task><UID>166</UID><ID>166</ID><Name>CUTOVER-2 Stand up the gateway-pointed test harness on the AWS test VM</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-02T08:00:00</Start><Finish>2026-06-03T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Take the existing api-tests/ harness (run-tests.sh: lint -&gt; Prism mock -&gt; smoke.sh 17 endpoints -&gt; Schemathesis -&gt; simulator.py 4 scenarios) on test VM i-02c3ebb814234cb3b and parameterize --base-url and the OAuth2 bearer so the identical suite can target (a) the Prism mock, (b) bootstrap S3-drop data, and (c) TEEP's real gateway as endpoints land. Add a CI-style summary that records pass/fail per endpoint per target. No spec changes — this is the cutover-verification rig the brief promises ('same code, different --base-url').</Notes><PredecessorLink><PredecessorUID>165</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>167</UID><ID>167</ID><Name>CUTOVER-3 Smoke + simulator dry-run against bootstrap S3-drop data</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-08T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Before any gateway exists, point the harness at the bootstrap S3 JSON drops (shaped exactly like the future REST responses, per the brief) for all four read systems plus the Sierra-xlsx Sensirion 'bootstrap-0'. Confirm the agent makes the right calls in the right order and the auto-close/dispatch decision logic fires correctly on real-shaped data. This validates the config-only cutover claim: if it passes on S3-drop shapes it will pass on the gateway with only a base-URL change. Surface any shape drift between the S3 drops and teep-api.yaml back to the bootstrap/asset-registry workstream.</Notes><PredecessorLink><PredecessorUID>166</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>168</UID><ID>168</ID><Name>CUTOVER-11 Production monitoring wiring — Prometheus to CloudWatch + Integration Health screen</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-09T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Wire the production observability the success criteria depend on: emit and ship teep_api_calls_total{system,endpoint,status}, teep_api_latency_ms, teep_api_errors_total{system,reason} plus the FR-35 agent KPIs (events/day, classification-mix, MTTA, MTTR, auto-close-rate, dispatch-to-close time, TaskHub open-&gt;close latency) from Prometheus to CloudWatch in Taikun's us-east-1 account. Light up the existing Integration Health screen (mockups-html/04-integration-health.html) against live metrics so per-system up/down/latency is visible to Darko and Mike. Confirm logs remain metadata-only (endpoint/status/latency/hash — no raw bodies) per the residency commitment.</Notes><PredecessorLink><PredecessorUID>166</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>169</UID><ID>169</ID><Name>CUTOVER-4 Per-system contract conformance test as each gateway endpoint lands</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-05T08:00:00</Start><Finish>2026-06-10T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>As TEEP's gateway exposes each system's endpoints, run the smoke subset for that system against the real gateway base URL with real OAuth2 client-credentials. Verify status codes, content-type, RFC7807 problem+json on errors (the one thing Prism could not reproduce), Idempotency-Key handling on writes, and HMAC webhook signature verification (X-TEEP-Signature + X-TEEP-Timestamp, +/-5min skew). Run once per system (Sensirion, Cygnet-via-intermediary, ProCount, Carte, TaskHub-read, TaskHub-write). File any conformance gaps as gateway defects to the gateway workstream (GW-*).</Notes><PredecessorLink><PredecessorUID>166</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>170</UID><ID>170</ID><Name>CUTOVER-12 Failure-notification channel wiring (email default / Teams / PagerDuty)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-09T08:00:00</Start><Finish>2026-06-10T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Wire the alerting the brief and security-answers doc specify: threshold of &gt;=5 failures within 5 min for the same system raises an alert to the designated TEEP contact; auth failures (401/403) rotate-retry-once-then-alert; &gt;15-min system-unavailable triggers degraded mode (flag in-flight events Undetected, notify MRO that automated triage is degraded, keep Sierra's dashboard up, never silently drop or invent a classification). Default channel is email; wire Teams or PagerDuty/Opsgenie if TEEP provides an integration key. Requires Darko to name the contact + channel (OD-2).</Notes><PredecessorLink><PredecessorUID>168</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>171</UID><ID>171</ID><Name>CUTOVER-10 Email-fallback path verification (TaskHub-write-slips contingency)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-12T08:00:00</Start><Finish>2026-06-12T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Implement-and-verify the brief's safety valve: if the TaskHub write API is not ready by go-live, the agent emails the MRO team the full evidence pack instead of POSTing a TaskHub task, config-toggled per environment. Verify the email-fallback produces the same evidence pack, that the office-cleared auto-close path is fully unaffected (it needs no write), and that toggling back to TaskHub-write is a config flip. This lets the read-system pilot and the 56.5% office-cleared value start on time even if write slips.</Notes><PredecessorLink><PredecessorUID>165</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>172</UID><ID>172</ID><Name>CUTOVER-13 SSO production-cutover gate verification (TEEP users into the Taikun app)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-12T08:00:00</Start><Finish>2026-06-15T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Verify, jointly with Sahir/TEEP IT, that TEEP users can sign into the Taikun platform with corporate Entra ID credentials before declaring full production cutover: enterprise app registration in the TEEP Entra tenant, redirect/reply URLs, claims + group-&gt;role mapping (Devin/MRO, Sierra/HSE-reporting, Clovis/ops, Darko-Mike/integration-health), optional SCIM provisioning, and conditional-access review passed. This workstream consumes the SSO build (SSO-* / identity workstream) and gates production user access — until it passes, the pilot dashboards are accessed via the Taikun-managed login on the demo VM, not TEEP corporate identity.</Notes><PredecessorLink><PredecessorUID>168</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>173</UID><ID>173</ID><Name>CUTOVER-14 Bedrock production-LLM cutover verification (latency + data-residency)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-12T08:00:00</Start><Finish>2026-06-15T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Verify the agent's LLM inference runs through TEEP's AWS Bedrock (replacing the default direct-Anthropic path for production) before the auto-close/classification path is declared production-ready: cross-account IAM access working, required Claude model(s) enabled in the TEEP Bedrock region, the Taikun LiteLLM gateway pointed at Bedrock, and end-to-end classification of &gt;=20 known Jan-2026 alerts produces the same decisions as the validated path within acceptable latency. Confirm data-residency (TEEP-governed Bedrock for LLM calls). Consumes the Bedrock build (BEDROCK-* workstream). Gates the auto-close/classification go-live because every classification is an LLM call.</Notes><PredecessorLink><PredecessorUID>166</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>174</UID><ID>174</ID><Name>CUTOVER-5 Sensirion go-live slice — live webhook ingestion verification</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-12T08:00:00</Start><Finish>2026-06-15T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Verify the latency-critical path: TEEP/Nubo POSTs a signed Sensirion event to Taikun's webhook endpoint, the HMAC signature validates within the +/-5min skew window, and the agent triggers triage in seconds (the brief's ~4-sec target, replacing the 5.2h email delay). Replay a known Jan-2026 event end-to-end and confirm emissions.alerts INSERT with status=Open + email_received timestamp. Confirm the GET poll fallback works if a webhook is missed. Depends on the Sensirion integration workstream pinning device poll cadence and device-&gt;pad/well mapping.</Notes><PredecessorLink><PredecessorUID>169</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>170</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>175</UID><ID>175</ID><Name>CUTOVER-7 ProCount + Carte go-live slice — codes/comments/injection read validation</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-12T08:00:00</Start><Finish>2026-06-15T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Validate ProCount down/up codes, operator comments, and work orders (56/168 alerts) and Carte injection-rate series (22/168, optional) against the real gateway. Confirm whether Carte's injection data is satisfiable through the ProCount API (per the open question to the IFS Merrick owner) — if so, drop the separate Carte endpoint from the go-live checklist. Spot-check codes/comments against known alerts to confirm the evidence pack the agent assembles matches what Devin reads manually today.</Notes><PredecessorLink><PredecessorUID>169</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>176</UID><ID>176</ID><Name>CUTOVER-6 Cygnet-via-existing-intermediary go-live slice — SCADA signal validation</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-16T08:00:00</Start><Finish>2026-06-18T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>TEEP does NOT permit a direct CygNet API. With Mike + Darko, confirm the chosen intermediary pipe (FMP/TaskHub ~30-min device poll, ProCount's documented Cygnet integration, or a historian/read-replica/curated export) and validate the adapted Cygnet data contract delivers tubing/line/casing pressure, sales rate, and compressor metrics plus the 4h pre-event window. Spot-check against the 95/168 alerts that cite Cygnet signals: confirm the pressure-drop signatures the auto-close logic depends on are present and correct in the intermediary feed. Note: the intermediary's coarser cadence (e.g. ~30 min) may blunt the 4h-pre-event-window fidelity — record actual achievable resolution.</Notes><PredecessorLink><PredecessorUID>169</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>172</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>177</UID><ID>177</ID><Name>CUTOVER-9 TaskHub write go-live — idempotency + duplicate-dispatch soak</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-16T08:00:00</Start><Finish>2026-06-18T17:00:00</Finish><Duration>PT24H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>The single highest-consequence cutover: a duplicate POST /v1/fmp/tasks creates a duplicate real field-dispatch task in front of a lease operator. Before enabling write in production, soak-test against the real TaskHub write API: (1) POST create with Idempotency-Key, then replay the same key on a simulated 5xx retry and confirm TEEP returns the original task (no duplicate) within the 24h replay window; (2) PATCH add-note and PATCH close idempotency; (3) the task.updated inbound webhook round-trip (POST -&gt; LO updates -&gt; webhook in -&gt; agent reads final notes -&gt; PATCH closed). Run the dispatch, monitor, and timeout simulator scenarios against the live write API. Coordinate with Sebastian (builds the API) and Brent (confirms the dispatch task content matches LO expectations).</Notes><PredecessorLink><PredecessorUID>169</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>173</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>178</UID><ID>178</ID><Name>CUTOVER-8 Bootstrap-to-gateway base-URL cutover per read system (config-only)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-19T08:00:00</Start><Finish>2026-06-19T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>For each read system that passes its conformance slice (CUTOVER-5/6/7), flip Maxwell's data source from the bootstrap S3-drop reader to the gateway base URL via configuration only — no code change, per the brief's 'identical JSON shape means cutover is config-only' guarantee. Do this one system at a time with the prior bootstrap source retained as instant rollback. Re-run that system's smoke + the relevant simulator scenario immediately after each flip to confirm parity.</Notes><PredecessorLink><PredecessorUID>174</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>176</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>175</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>179</UID><ID>179</ID><Name>CUTOVER-15 Full end-to-end production rehearsal (all systems, all 4 scenarios)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-22T08:00:00</Start><Finish>2026-06-23T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>With read systems cut over to the gateway (CUTOVER-8), TaskHub write live or email-fallback armed (CUTOVER-9/10), SSO (CUTOVER-13) and Bedrock (CUTOVER-14) gates passed, and monitoring+alerting live (CUTOVER-11/12): run a full production rehearsal of all four scenarios (auto-close, dispatch, monitor, timeout) against the real stack using replayed Jan-2026 events. This is the single go/no-go rehearsal before declaring production. Confirm the complete close-the-loop: detect -&gt; 4 parallel reads -&gt; classify (via Bedrock) -&gt; act -&gt; monitor -&gt; close -&gt; Sierra's 22 columns populated with zero transcription.</Notes><PredecessorLink><PredecessorUID>178</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>177</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>168</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>170</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>172</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>173</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>180</UID><ID>180</ID><Name>CUTOVER-16 Production go-live + start of 60-day operate window</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-24T08:00:00</Start><Finish>2026-06-24T17:00:00</Finish><Duration>PT8H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Declare production: live Sensirion events trigger Maxwell end-to-end, the office-cleared auto-close path runs without human touch, dispatch tasks flow to TaskHub (or email-fallback), and the 60-day pilot clock starts. Establish the operate cadence: daily Integration Health glance, on-call ownership for the failure-notification channel, and the KPI dashboard tracking MTTA, auto-close rate, classification accuracy, and per-system API error rate against the success criteria.</Notes><PredecessorLink><PredecessorUID>179</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>181</UID><ID>181</ID><Name>CUTOVER-17 Weekly mapping-table review with Sierra (operate window)</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-26T08:00:00</Start><Finish>2026-07-01T17:00:00</Finish><Duration>PT32H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Per PRD risk mitigation (HSE/EPA category drift) and FR-29, run a weekly review with Sierra of the free-text-to-category mapping table (LO note -&gt; resolution_type / equipment / equipment_component / epa_identifier) during the operate window. Capture new LO phrasings, adjust the mapping config (not code — columns are configuration), and re-validate that the monthly Excel export still matches her exact 22-column template. This keeps classification accuracy on track toward the &gt;=92% Phase-1 target and ensures her report stays a drop-in replacement.</Notes><PredecessorLink><PredecessorUID>180</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>182</UID><ID>182</ID><Name>CUTOVER-18 Operate-window monitoring + incident handling against success criteria</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-06-26T08:00:00</Start><Finish>2026-07-03T17:00:00</Finish><Duration>PT48H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>Run the 60-day operate window watching the success criteria live: &gt;=95% Sensirion events ingested without human intervention, median MTTA &lt;=15 min, &gt;=40% auto-closed, Sierra's report generated entirely from the agent store, and zero API incidents forcing Darko to roll back access. Triage any per-system degradation via the notification channel + degraded-mode behavior; log every API incident and its resolution. Capture the operating data that, with the historical pull, firms the KPI commitments (the 22-day sample was too short).</Notes><PredecessorLink><PredecessorUID>180</PredecessorUID><Type>1</Type></PredecessorLink></Task>
<Task><UID>183</UID><ID>183</ID><Name>CUTOVER-19 Pilot review + Phase-2 go/no-go decision</Name><OutlineLevel>2</OutlineLevel><Manual>1</Manual><Start>2026-07-03T08:00:00</Start><Finish>2026-07-06T17:00:00</Finish><Duration>PT16H0M0S</Duration><DurationFormat>7</DurationFormat><Notes>At the end of the 60-day window, run the pilot review with Clovis, Steve, Darko, Sierra, and Devin against the 6 success criteria. Secure the written confirmation from Devin and Sierra that they would not return to the manual process (criterion 6). Present KPI actuals vs targets, recommend whether to recalibrate the auto-close target from the Phase-1 40% toward the Phase-2 65% using the accumulated operate-window + historical data, and produce the Phase-2 proposal (operator response handler, LLM free-text reconciliation when rule confidence &lt;0.7, SMS/Teams escalation channels, pre-event Cygnet anomaly detection).</Notes><PredecessorLink><PredecessorUID>181</PredecessorUID><Type>1</Type></PredecessorLink><PredecessorLink><PredecessorUID>182</PredecessorUID><Type>1</Type></PredecessorLink></Task>
</Tasks></Project>