toga-ai 1.0.451 → 1.0.453

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,6 +10,7 @@
10
10
  | [Nested FK object embedding is gated by the CHILD record's own ACL](features/nested-fk-acl-embedding.md) | When the V2 JSON engine serializes a foreign-key field into a **nested object** (in `getFullModelData()`, ~V2.php L6016-6060), it re-checks the **child** record | api2/Component/Api/V2/V2.php, dbchanges2/Client_Compass/2026-07-23b - PurchaseOrdersRecordReadAcl.sql |
11
11
  | [Nested-relationship writes & child matching (link vs. create)](features/nested-relationship-writes.md) | When a 2.0 API write payload (`POST`/`PUT`) contains a **nested related object** (e.g. | api2/Component/Api/V2/V2.php |
12
12
  | [Record Scripts (computed/aggregate /v2 endpoints — the authoring contract)](features/record-scripts.md) | In api2 you almost never write a controller. | api2/Component/Api/V2/V2.php, _underscore/Model/Team/Sprint.php, _underscore/Query.php |
13
+ | [V2 Request Logging & Where Requests Land (client vs core log DB)](features/request-logging.md) | The V2 engine logs **every inbound request** — success *and* failure, with response code and payload — and routes each log entry to one of two places depending | api2/Component/Api/V2/V2.php, _underscore/Model/Client/Logs/Api.php, _underscore/Model/Core/Logs/Api.php |
13
14
  | [POST + JSON-body args for scripted APIs](features/scripted-api-post-body-args.md) | The V2 engine can run a Record Script (scripted API) for a **POST** request, and a scripted API can receive its arguments from the **JSON request body** instead | api2/Component/Api/V2/V2.php |
14
15
  | [TOGa IQ Sprint Dashboard API (Record Scripts)](features/sprint-dashboard-api.md) | The internal **TOGa IQ sprint dashboard** is served in production by **six api2 Record Scripts** on `_Model_Team_Sprint` (`_underscore/Model/Team/Sprint.php`), | _underscore/Model/Team/Sprint.php, api2/Component/Api/V2/V2.php, dbchanges2/Core/2026-07-24a - SprintDashboardRecordScripts.sql, dbchanges2/Client_True/2026-07-24b - SprintDashboardScriptAcl.sql |
15
16
  | [Surface action-state via the surface=<slug> request option (M2M-safe)](features/surface-meta-option.md) | An opt-in V2 engine request option, `surface=<slug>`, that attaches per-record UI action state (`isVisible`/`isEnabled`) to a GET response **under `meta.surface | api2/Component/Api/V2/V2.php, _underscore/Model/Core/Surface.php |
@@ -0,0 +1,60 @@
1
+ ---
2
+ title: V2 Request Logging & Where Requests Land (client vs core log DB)
3
+ framework: "2.0"
4
+ repo: api2
5
+ project: API
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-07-28
10
+ owners: ["mhammontree"]
11
+ files:
12
+ - api2/Component/Api/V2/V2.php
13
+ - _underscore/Model/Client/Logs/Api.php
14
+ - _underscore/Model/Core/Logs/Api.php
15
+ related:
16
+ - ../architecture.md
17
+ - ../../../../clients/aig/features/entitlement-intake.md
18
+ ---
19
+
20
+ ## Summary
21
+
22
+ The V2 engine logs **every inbound request** — success *and* failure, with response code and
23
+ payload — and routes each log entry to one of two places depending on **how far the request got
24
+ before it failed**. If you audit a client's API traffic by looking only at `Logs_<Client>`, you
25
+ will miss pre-scope failures and file-based logs. This doc says where requests actually land.
26
+
27
+ ## How it works
28
+
29
+ In `api2/Component/Api/V2/V2.php` (~L2168) every request is logged and routed:
30
+
31
+ - **Request resolves to a client** → written to the **CLIENT log DB** via
32
+ `_Model_Client_Logs_Api` → table `Logs_<Client>.Api` (e.g. `Logs_Aig.Api`).
33
+ - **Request fails *before* client scoping** (bad/expired auth, unknown client — e.g. auth
34
+ errors `EN-1` / `EN-4`) → written to the **CORE log DB** via `_Model_Core_Logs_Api` → table
35
+ `Logs_2.Api` (for the 2.0 Core). Pre-scope failures therefore land in **Core** logs, **not**
36
+ the client log.
37
+ - There is also a **config-gated branch** (~L2166): when `_Config::api('log_filepath')` is set,
38
+ the log entry is written to a **file** on the host instead of the DB.
39
+
40
+ ## Blind spots when auditing a client's API traffic
41
+
42
+ Looking only at `Logs_<Client>.Api` misses three sources:
43
+
44
+ 1. **Pre-scope failures** — auth/unknown-client failures are in the **Core** log DB (`Logs_2.Api`),
45
+ because scoping never resolved to the client.
46
+ 2. **File-based logs** — if `log_filepath` is configured, entries are on the host filesystem,
47
+ not in any log DB.
48
+ 3. **Non-API loads** — anything that reaches the data via a worker, a `dbchanges2` migration, or
49
+ a direct DB write **never touches api2**, so it produces **no** api2 log entry at all.
50
+
51
+ This is exactly what made the AIG audit (TRUE-80179) non-obvious: the only `POST /v2/entitlements`
52
+ attempts anywhere were 7 dev/QA tests in the **Core** log (`Logs_2`), while the client's real
53
+ `Client_Aig` entitlements came from a bulk load that never went through api2 — see
54
+ [AIG entitlement intake → Intake reality](../../../../clients/aig/features/entitlement-intake.md).
55
+
56
+ ## Change history
57
+
58
+ - 2026-07-28 — Documented from the TRUE-80179 AIG log audit: V2 logs every request and routes it
59
+ to `Logs_<Client>` (post-scope) or `Logs_2` Core (pre-scope failure), with a config-gated
60
+ file-log branch. Captured the three audit blind spots. (mhammontree)
@@ -19,7 +19,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
19
19
 
20
20
  - **_underscore** (_Underscore) _(framework core)_ — 39 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
21
21
  - **worker2** (Worker) — 31 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
22
- - **api2** (API) — 17 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
22
+ - **api2** (API) — 18 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
23
23
  - **dbchanges2** (Database Changes) _(framework core)_ — 3 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
24
24
  - **toga2-supply** (TOGa Supply) — 3 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
25
25
  - **saml** (SAML SSO Gateway) — 3 doc(s) → [2.0/apps/saml/INDEX.md](2.0/apps/saml/INDEX.md)
@@ -2,5 +2,6 @@
2
2
 
3
3
  | Doc | Framework | Summary | Files |
4
4
  |-----|-----------|---------|-------|
5
- | [AIG Entitlement Intake & SaleItem Code Resolution](features/entitlement-intake.md) | 2.0 | AIG protection-plan entitlements arrive as `api2` V2 JSON POSTs. | _underscore/Model/Aig/Entitlement.php, dbchanges2/Client_Aig/2026-06-18a - TRUE-79534 AIG SaleItem codes.sql |
5
+ | [AIG Contract Reconciliation & Dealer Programs (STS_001 / SA_001)](features/contract-reconciliation.md) | 2.0 | How to reconcile an **AIG contract sales sheet** against the `Client_Aig` tenant, and the durable finding that the **Staples Advantage.com (`SA_001`) program is | _underscore/Model/Client/Entitlement.php, _underscore/Model/Aig/Entitlement.php, _underscore/Model/Client/Contact.php, _underscore/Model/Aig/Contact.php |
6
+ | [AIG Entitlement Intake & SaleItem Code Resolution](features/entitlement-intake.md) | 2.0 | The `_Model_Aig_Entitlement::prePost` interceptor below is the code path defined for AIG protection-plan entitlements arriving as `api2` V2 JSON POSTs. | _underscore/Model/Aig/Entitlement.php, dbchanges2/Client_Aig/2026-06-18a - TRUE-79534 AIG SaleItem codes.sql |
6
7
  | [AIG (Staples Protection Plan)](profile.md) | 2.0 | AIG is the warranty underwriter behind the **Staples Protection Plan** retail program. | |
@@ -0,0 +1,85 @@
1
+ ---
2
+ title: AIG Contract Reconciliation & Dealer Programs (STS_001 / SA_001)
3
+ framework: "2.0"
4
+ project: API
5
+ client: aig
6
+ type: client-feature
7
+ status: active
8
+ updated: 2026-07-28
9
+ owners: ["mhammontree"]
10
+ files:
11
+ - _underscore/Model/Client/Entitlement.php
12
+ - _underscore/Model/Aig/Entitlement.php
13
+ - _underscore/Model/Client/Contact.php
14
+ - _underscore/Model/Aig/Contact.php
15
+ related:
16
+ - clients/aig/features/entitlement-intake.md
17
+ - ../../../2.0/apps/api2/features/request-logging.md
18
+ ---
19
+
20
+ ## Summary
21
+
22
+ How to reconcile an **AIG contract sales sheet** against the `Client_Aig` tenant, and the
23
+ durable finding that the **Staples Advantage.com (`SA_001`) program is entirely absent from
24
+ `Client_Aig`** (TRUE-80179). AIG runs **two dealer programs under the one `Client_Aig`
25
+ tenant**, distinguished by `DealerId` in AIG's own data:
26
+
27
+ | DealerId | Program |
28
+ |---|---|
29
+ | `STS_001` | **Staples Technology Solutions** |
30
+ | `SA_001` | **Staples Advantage.com** |
31
+
32
+ Only `STS_001` contracts are present in `Client_Aig`; `SA_001` was never ingested.
33
+
34
+ ## Match keys (sales sheet → `Client_Aig`)
35
+
36
+ Empirically validated against 448 clean matches (consistent base-plan + coverage dates):
37
+
38
+ | AIG sales-sheet column | `Client_Aig` field | Notes |
39
+ |---|---|---|
40
+ | **AIG Contract Number** (13-digit, `1000…` family, e.g. `1000046237903`) | `Entitlements.number` | the primary contract identity |
41
+ | **Client Contract Number** (e.g. `7679463206-2`) | `Entitlements.receiptNumber` | also embedded in `vendorEntitlementNumber` as `<dealerPrefix>_<clientContract>` |
42
+
43
+ ## Active / lifecycle semantics
44
+
45
+ - **Sales-sheet "Active"** = the **Cancel Date is blank** (there is no explicit active flag on
46
+ the AIG sheet).
47
+ - **Contact active flag** = `Contacts.isActive`.
48
+ - **Entitlement lifecycle** is `entitlementStageId`. In `Client_Aig` this is **currently all
49
+ NULL**, so entitlement stage is **not** a reliable active signal for this tenant — use
50
+ contract cancel-date / `Contacts.isActive` instead.
51
+
52
+ ## The SA_001 gap (TRUE-80179)
53
+
54
+ Reconciling a weekly AIG sales sheet (**1,149 contracts** across `STS_001` + `SA_001`) against
55
+ `Client_Aig` (**430 entitlements**):
56
+
57
+ - **448 matched**, **0 DB-only**, **701 missing**.
58
+ - The 701 missing = **156 active `STS_001`** + **545 `SA_001`** (506 of them active).
59
+ - The entire `SA_001` feed is **absent from `Client_Aig`** — confirmed absent from **all** local
60
+ tenant DBs, not just this one.
61
+
62
+ **Real support failure that surfaced it:** a customer (client contract `7679463206-2`, AIG
63
+ contract `1000046237903`, an **active 2YR Printer** plan) called in and could not be found,
64
+ because their `SA_001` contract was never ingested.
65
+
66
+ **Open scope question (unresolved this session):** is `SA_001` (Staples Advantage.com)
67
+ *supposed* to route into `Client_Aig` at all, or is it a separate program with its own
68
+ destination? This needs an answer from AIG / the team before back-filling — do not assume the
69
+ gap is simply a missed load.
70
+
71
+ ## How to load a missing program / sheet
72
+
73
+ If `SA_001` (or any new sheet) is confirmed in-scope for `Client_Aig`, it is loaded the same way
74
+ the SaleItem catalog is — a `dbchanges2/Client_Aig/` migration with a session-scoped temp table
75
+ + anti-join (see [entitlement-intake.md → Uploading new codes](entitlement-intake.md#uploading-new-codes-the-recurring-task)),
76
+ **not** via the API. See that doc's *Intake reality* for why no live API feed exists.
77
+
78
+ ## Change history
79
+
80
+ - 2026-07-28 — TRUE-80179: reconciled a 1,149-contract AIG sheet vs `Client_Aig` (448 matched,
81
+ 701 missing). Documented the `STS_001`/`SA_001` dealer-program split, the empirically-validated
82
+ match keys (AIG Contract Number → `number`, Client Contract Number → `receiptNumber`), the
83
+ active/`entitlementStageId`-NULL semantics, and the finding that the whole `SA_001` program is
84
+ missing from `Client_Aig` (drove a real "customer not found" support call). Scope of `SA_001`
85
+ left open for AIG/team. (mhammontree)
@@ -5,12 +5,14 @@ project: API
5
5
  client: aig
6
6
  type: client-feature
7
7
  status: active
8
- updated: 2026-07-09
8
+ updated: 2026-07-28
9
9
  owners: ["mhammontree"]
10
10
  files:
11
11
  - _underscore/Model/Aig/Entitlement.php
12
12
  - dbchanges2/Client_Aig/2026-06-18a - TRUE-79534 AIG SaleItem codes.sql
13
13
  related:
14
+ - clients/aig/features/contract-reconciliation.md
15
+ - 2.0/apps/api2/features/request-logging.md
14
16
  - 2.0/apps/api2/features/nested-relationship-writes.md
15
17
  - 2.0/apps/api2/architecture.md
16
18
  - 2.0/apps/dbchanges2/architecture.md
@@ -19,13 +21,42 @@ related:
19
21
 
20
22
  ## Summary
21
23
 
22
- AIG protection-plan entitlements arrive as `api2` V2 JSON POSTs. Before the entitlement is
23
- written, the `_Model_Aig_Entitlement::prePost` interceptor resolves the **sold warranty SKU**
24
+ The `_Model_Aig_Entitlement::prePost` interceptor below is the code path defined for AIG
25
+ protection-plan entitlements arriving as `api2` V2 JSON POSTs. **In reality no live API feed
26
+ runs — contracts are loaded into `Client_Aig` as a periodic bulk load, the same way the
27
+ SaleItem catalog is (see [Intake reality](#intake-reality-how-contracts-actually-arrive),
28
+ TRUE-80179).** On a V2 POST, before the entitlement is written, the interceptor resolves the
29
+ **sold warranty SKU**
24
30
  (`saleItem.partNumber`) against `Client_Aig.Items` to set the `Entitlement.saleItemId` foreign
25
31
  key and to attach fulfillment types/methods. If that part number has no matching `Items` row,
26
32
  the FK can't resolve and intake fails with **"Missing AIG item ID."** Keeping
27
33
  `Client_Aig.Items` populated with AIG's current sale-item catalog is what keeps intake working.
28
34
 
35
+ ## Intake reality (how contracts actually arrive)
36
+
37
+ The API-intake path described in *How it works* is **real code but is not the live feed.** An
38
+ audit of `Logs_Aig.Api` over 2025-09-02 → 2026-07-28 (TRUE-80179) found:
39
+
40
+ - **Zero entitlement-creation calls** — no `POST /v2/entitlements`, no PUT upserts, no cXML.
41
+ `/v2/entitlements` sees **GETs only** (46,627 reads). `STS_001` / `SA_001` / "Staples
42
+ Advantage" appear in **zero inbound payloads**.
43
+ - The existing **430** `Client_Aig` entitlements were all created in a **single Sept-2025 bulk
44
+ load** (all `dtCreated` 2025-09, purchase dates Mar–Aug 2025) — an out-of-band
45
+ migration/script, **not** an API feed. **No AIG entitlement-creating worker exists in
46
+ worker2.**
47
+ - The only `POST /v2/entitlements` calls **anywhere** are **7 developer/QA test attempts** in the
48
+ 2.0 **Core** log (`Logs_2`, not the client log), 2026-04-10 & 04-13 from `127.0.0.1` with dummy
49
+ data ("XYZ Vendor / Jane Doe"), **all failed** (401 missing/expired token; 500 "Subscription
50
+ not found/not valid"). Pre-scope auth failures land in Core logs — see
51
+ [V2 request logging](../../../2.0/apps/api2/features/request-logging.md).
52
+
53
+ **Practical takeaway:** AIG intake is a **periodic bulk load**, not the live API push this doc
54
+ originally implied. A new AIG sales sheet is loaded like the SaleItem catalog — a
55
+ `dbchanges2/Client_Aig/` migration (staged temp table + anti-join), **not** the API. Do **not**
56
+ chase a nonexistent "intake bug" or wait on an API feed that does not run. For reconciling a
57
+ sales sheet against the tenant (and the `SA_001` program gap), see
58
+ [AIG contract reconciliation](contract-reconciliation.md).
59
+
29
60
  ## Key files / entry points
30
61
 
31
62
  - **`_underscore/Model/Aig/Entitlement.php`** — `prePost(&$api, &$payload)` is the PRE-POST
@@ -153,6 +184,12 @@ this interceptor or use this dual-purpose Items pattern.
153
184
 
154
185
  ## Change history
155
186
 
187
+ - 2026-07-28 — TRUE-80179: corrected the intake premise. A `Logs_Aig.Api` audit (Sept 2025 →
188
+ Jul 2026) found **zero** entitlement-creation API calls — all 430 `Client_Aig` entitlements
189
+ came from a single Sept-2025 bulk load, and the only `POST /v2/entitlements` calls anywhere are
190
+ 7 failed dev/QA tests in the Core `Logs_2`. Intake is a periodic bulk load, not a live API
191
+ feed. Added *Intake reality* + the API-vs-bulk gotcha, and linked the new
192
+ contract-reconciliation and V2 request-logging docs. (mhammontree)
156
193
  - 2026-07-09 — TRUE-79534 SaleItem load reworked: the 2,200-code `INSERT ... SELECT FROM
157
194
  (<2,199 `UNION ALL` links>)` derived table failed at parse time with ERROR 1436 (Thread
158
195
  stack overrun). Rebuilt as a `TEMPORARY` staging table filled by a flat multi-row `VALUES`
@@ -0,0 +1,71 @@
1
+ ---
2
+ type: session
3
+ slug: true-80179-aig-reconciliation
4
+ title: AIG Staples sales-sheet ↔ Client_Aig reconciliation
5
+ author: mhammontree
6
+ repos: [_underscore, api2]
7
+ framework: "2.0"
8
+ client: aig
9
+ status: active
10
+ created: 2026-07-28
11
+ updated: 2026-07-28
12
+ ---
13
+
14
+ # Session: true-80179-aig-reconciliation
15
+ **Date:** 2026-07-28
16
+ **Project/Repo:** api2 / _underscore (2.0), client AIG (Staples Protection Plan)
17
+ **Task:** TRUE-80179 — verify an AIG weekly sales .xlsx against `Client_Aig` entitlements, quantify gaps, and determine how (and whether) the missing contracts get ingested.
18
+
19
+ ---
20
+
21
+ ## What WORKED
22
+ <!-- Include specific file paths and evidence -->
23
+ - **Reconciliation method (validated):** key spreadsheet **AIG Contract # → `Entitlements.number`** (proven by 448 clean matches with consistent base-plan + coverage dates), **Client Contract # → `Entitlements.receiptNumber`** (also embedded in `vendorEntitlementNumber` as `<dealerPrefix>_<clientContract>`). "Active" on the sheet = **Cancel Date blank**.
24
+ - **Dependency-free xlsx parsing** via PowerShell `System.IO.Compression` (scratchpad `inspect-xlsx.ps1`, `recon.ps1`, `recon2.ps1`) — parsed all 1,149 detail rows, converted Excel serial dates (epoch 1899-12-30). No Excel/module needed.
25
+ - **Read-only local MySQL** via `C:/xampp_8x/mysql/bin/mysql.exe -u root` (root/no password).
26
+ - **Reconciliation result:** sheet has TWO dealer programs — STS_001 (Staples Technology Solutions, 604 rows) + SA_001 (Staples Advantage.com, 545 rows). vs `Client_Aig` 430 entitlements: **448 matched, 0 DB-only, 701 missing** (156 active STS_001 + 545 SA_001 [506 active]). Matched data consistent: 0 base-plan / 0 coverage-end / 0 purchase-date mismatches; only 5 benign coverage-start diffs (DB start = purchase date, sheet = +12 days).
27
+ - **Intake root-cause via `Logs_Aig.Api`** (user imported it locally; window 2025-09-02 → 2026-07-28): **ZERO entitlement-creation calls** (no POST/PUT `/v2/entitlements`, no cXML). `/v2/entitlements` = 46,627 GET reads only. The 430 existing entitlements ALL have `dtCreated` in 2025-09 → a single out-of-band **bulk load**, not an API feed. No AIG entitlement-creating worker in `worker2`.
28
+ - **Logging mechanism verified in code** (`api2/Component/Api/V2/V2.php` ~L2168): every request logged (success + failure); routes to `Logs_<Client>` once client resolves, else 2.0 Core log `Logs_2`. Checked `Logs_2`: the only entitlement POSTs anywhere are **7 localhost dev/QA tests** (2026-04-10 & 04-13, 127.0.0.1, "XYZ Vendor/Jane Doe" dummy data), all failed (401 EN-1/EN-4, 500 "Subscription not found/not valid").
29
+ - **Caller confirmed:** contract `7679463206-2` (AIG# `1000046237903`, SA_001, active 2YR Printer plan) is on the sheet but absent from ALL local tenant DBs — why support couldn't find her.
30
+ - **Deliverables:** `c:/WWW/TRUE-80179_AIG_reconciliation.md` + 2 CSVs. `/capture` published 3 KB docs (see file state).
31
+
32
+ ## What did NOT work — DO NOT RETRY THESE
33
+ <!-- Exact failure reasons — do not vague-ify -->
34
+ - **DO NOT sample only the top of the sheet.** My first probe used 5 AIG numbers from the sheet's first rows (all `1000046…`, all SA_001) and every one was above the DB's max `number` → I wrongly concluded "the sets don't overlap at all / the program is absent entirely." The full 1,149-row run corrected this to **448 matches**. The sheet is multi-program; the head is all SA_001. Always run the full diff before concluding.
35
+ - **DO NOT run multiple payload `LIKE` scans in one batch against `Logs_Aig`.** 4 `requestPayload/responsePayload LIKE '%…%'` queries over 564K MEDIUMTEXT rows → **2-minute timeout (exit 143)**. Scope by indexed cols (`route`/`method`/`dtStamp`) first, or narrow the date range; run heavy scans one at a time.
36
+ - The first local import did **not** include `Logs_Aig` (only `Client_*` DBs) — had to have the user import it from prod before the API-log investigation was possible.
37
+ - The `Logs` DB (not `Logs_2`) is the **legacy 1.0** API log — uppercase `API` table, no `route`/`hostname` columns → `ERROR 1054 Unknown column`. Not usable for 2.0 api2 analysis; use `Logs_2` for 2.0 Core.
38
+
39
+ ## Not tried yet (candidates for next session)
40
+ - **Resolve the SA_001 scope question with AIG/team:** does Staples Advantage.com (`SA_001`) route into `Client_Aig` at all, or is it a separate program? (Gates any back-fill of the 545.)
41
+ - **Find how the Sept-2025 bulk load of the 430 was performed** — no `worker2` job exists; look for a one-off load script / migration, or check worker/CloudWatch logs around 2025-09-02. This is the mechanism to reuse for the 701.
42
+ - **Check file-based API logging** (`_Config::api('log_filepath')`) on the prod host — a blind spot the DBs can't see.
43
+ - **Investigate the 156 missing active STS_001** specifically: are their sale-item codes in `Client_Aig.Items`? Any intake failures? (These are the "same program that's already in the DB" gap.)
44
+ - **Draft the `dbchanges2/Client_Aig/` load migration** for the 701 (staged temp table + anti-join, generated UUIDs, keyed on `number`) — only after the scope answer and after verifying SA sale-item codes exist in `Client_Aig.Items`.
45
+ - **Pull an SA_001-only CSV** for support to reference in the interim.
46
+
47
+ ## Current file state
48
+ | File | Status | Notes |
49
+ |------|--------|-------|
50
+ | c:/WWW/TRUE-80179_AIG_reconciliation.md | Created | Full deliverable: reconciliation + caller + intake investigation + logging mechanism + 2 blind spots |
51
+ | c:/WWW/TRUE-80179_missing_from_db.csv | Created | The 701 contracts on the sheet but not in Client_Aig (actionable list) |
52
+ | c:/WWW/TRUE-80179_sheet_contracts.csv | Created | All 1,149 parsed contract rows, dates normalized |
53
+ | scratchpad/inspect-xlsx.ps1, recon.ps1, recon2.ps1 | Created (temp) | Read-only analysis scripts; session-temp scratchpad |
54
+ | knowledge/clients/aig/features/contract-reconciliation.md | Created (pushed) | STS_001/SA_001 split, match keys, 701 gap, unresolved SA_001 scope question |
55
+ | knowledge/2.0/apps/api2/features/request-logging.md | Created (pushed) | V2 logs every request → Logs_<Client> post-scope vs Logs_2 Core pre-scope; audit blind spots |
56
+ | knowledge/clients/aig/features/entitlement-intake.md | Updated (pushed) | Corrected false "arrives via API POST" premise → Intake reality section |
57
+
58
+ ## Decisions made
59
+ - **Match key = AIG Contract # ↔ `Entitlements.number`** (not `vendorEntitlementNumber`). Rationale: 448 empirical matches with aligned base-plan/coverage dates. Rejected: keying on `vendorEntitlementNumber`/`receiptNumber` (those hold `<dealer>_<clientContract>`, a different value).
60
+ - **Reconcile in-memory (read-only), no scratch DB tables.** Rationale: pure verification, avoid mutating the local DB. Rejected: staging table inside `Client_Aig`.
61
+ - **Deliverable kept as a local file, not a published Artifact.** Rationale: contains AIG client business data.
62
+ - **api2 request-logging documented as a `feature` doc, not an architecture edit** (session-capture's call). Rationale: discrete mechanism with code anchors, matches existing api2 feature-doc pattern. Could be moved onto `api2/architecture.md` if a senior prefers.
63
+
64
+ ## Blockers
65
+ - **SA_001 scope question is unresolved** — needs an answer from AIG/the team (does Staples Advantage.com belong in `Client_Aig`?). This gates any load/back-fill of the 545 SA_001 (and shapes whether the 701 is one problem or two).
66
+
67
+ ## Exact next step
68
+ > Get the AIG/team answer on whether `SA_001` (Staples Advantage.com) is supposed to route into `Client_Aig`. In parallel, determine how the Sept-2025 bulk load of the existing 430 was done (search for a load script/migration; check worker/CloudWatch logs ~2025-09-02) — that's the mechanism to reuse for loading the 701. Reference `c:/WWW/TRUE-80179_AIG_reconciliation.md` and `TRUE-80179_missing_from_db.csv`.
69
+
70
+ ---
71
+ _Saved by /session-save on 2026-07-28_
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.451",
3
+ "version": "1.0.453",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",