toga-ai 1.0.374 → 1.0.375

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.
@@ -2,7 +2,6 @@
2
2
 
3
3
  | Doc | Summary | Files |
4
4
  |-----|---------|-------|
5
- | [Proposed — ExternalIdentifiers map table for integration id ↔ external-system linkage](architecture/external-identifiers-map-table.md) | A **proposal / counter-proposal (status: proposed — nothing here is built)** that cross-system identifier linkage (e.g. | _underscore/Model/Forecast/Opportunity.php, worker2/Worker/Netsuite/Opportunity.php |
6
5
  | [Proposed — git-sourced base+overlay JSON authoring for the Surface layer](architecture/surface-authoring-proposal.md) | A **proposal / handoff recommendation** (not implemented) that the Surface layer's *authoring* model move off hand-authored SQL against the `SurfaceOverrides` E | _underscore/Model/Core/Surface.php, _underscore/Model/Client/SurfaceOverride.php |
7
6
  | [_underscore Framework Architecture](architecture.md) | `_underscore` is the shared PHP backend framework for **all 2.0 applications**. | _underscore/_underscore.php, _underscore/Loader.php, _underscore/Framework.php, _underscore/Model.php, _underscore/Database.php, _underscore/Query.php, _underscore/Route.php, _underscore/Component.php |
8
7
  | [ACL Permission Chain (Record & Field Authorization)](features/acl-permission-chain.md) | Authorization in the 2.0 API is **metadata-driven**: whether a role may Create/Read/Update/Delete a record is decided by rows across **four linked tables**, not | api2/Component/Api/V2/V2.php, _underscore/Model/Core/Page.php, dbchanges2/Client/2026-06-03- BLANK_CLIENT_DATABASE.sql, dbchanges2/Client/2026-06-23b - ItemTranslationsAcl.sql |
@@ -31,7 +31,6 @@ files:
31
31
  related:
32
32
  - ./netsuite-salesorder-open-orders-sync.md
33
33
  - ../architecture.md
34
- - ../../_underscore/architecture/external-identifiers-map-table.md
35
34
  ---
36
35
 
37
36
  ## Summary
@@ -178,9 +177,6 @@ tasks. The fix is a **race-safe DB claim** on the **existing `Forecast.Opportuni
178
177
  `NetsuiteOpportunityClickupTask` table in `Client_True`; reviewer Rohan Girish rejected that and mandated
179
178
  reusing `Forecast.Opportunities`.)
180
179
 
181
- A counter-proposal (ExternalIdentifiers map table) has been raised — see
182
- `_underscore/architecture/external-identifiers-map-table.md`; unresolved.
183
-
184
180
  How the claim is race-safe:
185
181
  - **Conditional UPDATE = the atomic claim.** A raw `_Query`:
186
182
  `UPDATE Opportunities SET clickupTaskId='<uuid sentinel>' WHERE netsuiteOpportunityInternalId=X AND
@@ -17,7 +17,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
17
17
 
18
18
  ## 2.0 framework
19
19
 
20
- - **_underscore** (_Underscore) _(framework core)_ — 35 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
20
+ - **_underscore** (_Underscore) _(framework core)_ — 34 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
21
21
  - **worker2** (Worker) — 30 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
22
22
  - **api2** (API) — 12 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)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.374",
3
+ "version": "1.0.375",
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",
@@ -1,97 +0,0 @@
1
- ---
2
- title: Proposed — ExternalIdentifiers map table for integration id ↔ external-system linkage
3
- framework: "2.0"
4
- repo: _underscore
5
- project: _Underscore
6
- client: shared
7
- type: architecture
8
- status: proposed
9
- updated: 2026-07-17
10
- owners: ["dfranks"]
11
- files:
12
- - _underscore/Model/Forecast/Opportunity.php
13
- - worker2/Worker/Netsuite/Opportunity.php
14
- related:
15
- - ../features/model-save-vs-query-atomic-update.md
16
- - ../../worker2/features/netsuite-opportunity-sync.md
17
- ---
18
-
19
- ## Summary
20
-
21
- A **proposal / counter-proposal (status: proposed — nothing here is built)** that cross-system
22
- identifier linkage (e.g. a NetSuite opportunity ↔ its ClickUp task id) live in a dedicated
23
- **`ExternalIdentifiers` map table** rather than as ad-hoc columns bolted onto whichever business
24
- table happens to be nearby. The immediate driver is the TRUE-80044 atomic ClickUp-task claim, whose
25
- currently-mandated approach adds a `clickupTaskId` column to `Forecast.Opportunities`. This doc is
26
- `dfranks`'s architectural counter-proposal to that mandate, raised for open discussion — **not** a
27
- silent supersede of the reviewer decision. See the rebuttal section below.
28
-
29
- **Critical framing:** This is a **proposal under open discussion**, not the current system and not a
30
- settled reversal. The mandated approach on record (a `clickupTaskId` column on
31
- `Forecast.Opportunities`) remains the standing decision until the disagreement is resolved with the
32
- reviewer.
33
-
34
- ## Design
35
-
36
- A single **source-generic, single-anchor** mapping table in the integration schema
37
- (`Client_True` / `DB_TRUE`, **not** Forecast). A source system + its record id anchor the entity; a
38
- target system + its id are the mapping. NetSuite is a `sourceSystem` *value* (`'netsuite'`), never a
39
- privileged column, and there is deliberately **no** cross-system correlation key (`entityUuid`):
40
- every integration asks the same single-anchor question — "for this source record, what's its
41
- ClickUp/Salesforce/… id?" — which needs no id-to-id web.
42
-
43
- - **Columns:** `id`, `entityType` (e.g. `opportunity`), `sourceSystem` (e.g. `netsuite`),
44
- `sourceId` (the source record id), `targetSystem` (e.g. `clickup`), `targetExternalId` (the value;
45
- `NULL` = claimed/in-flight), `dtCreated`/`dtUpdated`. No synthetic row `uuid` — `uq_claim` is the
46
- natural key.
47
- - **Uniqueness:** `UNIQUE (entityType, sourceSystem, sourceId, targetSystem)` = the atomic claim
48
- (one mapping per source record per target system); `UNIQUE (targetSystem, entityType,
49
- targetExternalId)` = no two source records share one target id (NULLs exempt, so many in-flight
50
- rows coexist).
51
- - **Atomic claim = one `INSERT IGNORE` + `getAffectedRows()`** — no sentinel value, no correlation
52
- step: the row's presence is the claim, `targetExternalId IS NULL` marks in-flight. Raw `_Query` is
53
- required because the ORM `save()` cannot express a conditional/affected-rows write — see
54
- [model-save-vs-query-atomic-update](../features/model-save-vs-query-atomic-update.md). Winner
55
- writes `targetExternalId` back via the model; failure path deletes the in-flight row
56
- (`… AND targetExternalId IS NULL`). Because the table stands alone, the claim has **no ordering
57
- dependency** on the Forecast import (unlike a column claim, which needs the opportunity row
58
- upserted first).
59
- - **Scales without schema change:** a new integration adds rows with a new `targetSystem` value; a
60
- new source adds a new `sourceSystem` value. Keyed on the source record id in the integration
61
- schema, the mapping is independent of the Forecast DB by construction.
62
-
63
- The `entityUuid`/cross-system-correlation variant is deliberately avoided — it only pays off if the
64
- *source* system (NetSuite) itself stopped being the identity anchor, which is not a concern here.
65
-
66
- ## Rebuttal to the clickupTaskId-column mandate
67
-
68
- `2.0/apps/worker2/features/netsuite-opportunity-sync.md` records reviewer **Rohan Girish** mandating
69
- (2026-07-15) that the ClickUp-task dedup claim be implemented as a **`clickupTaskId` column on
70
- `Forecast.Opportunities`**, and rejecting the earlier plan's dedicated table. This doc is `dfranks`'s
71
- **counter-proposal**, offered as an open architectural disagreement to be resolved *with* the
72
- reviewer — the mandate stands until then. The three rebuttal arguments:
73
-
74
- 1. **Separation of concerns.** A ClickUp task id is *integration-linkage* state, not a business
75
- attribute of a Forecast opportunity. Storing it on `Forecast.Opportunities` conflates the
76
- integration/mapping layer with the forecasting domain model.
77
- 2. **Forecast-schema dumping-ground.** Every future external linkage (a second external system, a
78
- different id kind) under the column approach means another one-off nullable column on a business
79
- table, accreting integration cruft on the Forecast schema. A map table absorbs all of them with
80
- no schema change per linkage.
81
- 3. **Forecast-sync lifecycle decoupling.** The integration mapping must be able to **outlive a
82
- possibly-discontinued Forecast sync.** Binding the ClickUp linkage to the Forecast opportunity row
83
- ties its lifetime to the Forecast importer; if that sync is ever retired or reworked, the mapping
84
- goes with it. A standalone map table keeps the NS↔ClickUp linkage durable independently of
85
- Forecast.
86
-
87
- This is framed as a disagreement to resolve, not a reversal already taken. If the reviewer position
88
- holds, the mandated column approach is what ships.
89
-
90
- ## Change history
91
-
92
- - 2026-07-17 — Proposed as a rebuttal to the reviewer-mandated clickupTaskId-column approach
93
- (TRUE-80044). (dfranks)
94
- - 2026-07-17 — Refined to a source-generic, single-anchor design: `(sourceSystem, sourceId,
95
- targetSystem)` key with a single `INSERT IGNORE` claim; dropped the `entityUuid` correlation/two-
96
- step as over-engineered for the stated concerns (future integrations + Forecast-sync decoupling),
97
- both of which the single-anchor key already satisfies. (dfranks)