toga-ai 1.0.263 → 1.0.265
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/knowledge/2.0/apps/_underscore/INDEX.md +1 -1
- package/knowledge/2.0/apps/_underscore/architecture.md +15 -2
- package/knowledge/2.0/apps/_underscore/features/acl-permission-chain.md +28 -2
- package/knowledge/2.0/apps/_underscore/features/carrier-shipping-labels.md +88 -15
- package/knowledge/2.0/apps/toga2-supply/architecture.md +8 -1
- package/knowledge/2.0/apps/toga2-supply/features/fulfill-and-ship.md +26 -24
- package/knowledge/2.0/standards/backend-testing.md +58 -0
- package/package.json +1 -1
- package/skills/kickoff/SKILL.md +50 -0
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
| [_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 |
|
|
6
6
|
| [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 |
|
|
7
7
|
| [Assortment Name Translation (AssortmentTranslations sidecar)](features/assortment-name-translation.md) | Serves Assortment (product-grouping) **names** in multiple languages by adding a per-language **sidecar** table `AssortmentTranslations`, reusing the platform's | _underscore/Model/Client/AssortmentTranslation.php, dbchanges2/Client/2026-06-26a - AssortmentTranslations.sql, dbchanges2/Core/2026-06-26a - AssortmentTranslationsRecord.sql, dbchanges2/Client/2026-06-26b - AssortmentTranslationsAcl.sql |
|
|
8
|
-
| [Carrier Shipping Labels (UPS/FedEx) & NetSuite Item Fulfillment](features/carrier-shipping-labels.md) | Backend mechanics behind TOGa Supply's Fulfill & Ship: buying a carrier label (UPS/FedEx), persisting it, and creating the NetSuite Item Fulfillment with tracki | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/ItemFulfillments/TrackingNumber.php, _underscore/Component/Library/LabelPdf/LabelPdf.php, _underscore/Component/Library/Carriers/Ups/Ups.php, _underscore/Component/Library/Carriers/Fedex/Fedex.php, _underscore/Trait/Netsuite/ItemFulfillment.php, _underscore/Trait/Netsuite/SalesOrder.php, _underscore/Component/Library/NetSuite/NetSuite.php, _underscore/Model/Client/TrackingNumber.php, _underscore/Model/Client/ShippingMethod.php, _underscore/Model.php, _underscore/Cloud.php |
|
|
8
|
+
| [Carrier Shipping Labels (UPS/FedEx) & NetSuite Item Fulfillment](features/carrier-shipping-labels.md) | Backend mechanics behind TOGa Supply's Fulfill & Ship: buying a carrier label (UPS/FedEx), persisting it, and creating the NetSuite Item Fulfillment with tracki | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/TrackingNumber.php, _underscore/Model/Client/ItemFulfillments/TrackingNumber.php, _underscore/Component/Library/LabelPdf/LabelPdf.php, _underscore/Component/Library/Carriers/ShipmentRequest/ShipmentRequest.php, _underscore/Component/Library/Carriers/Ups/Ups.php, _underscore/Component/Library/Carriers/Fedex/Fedex.php, _underscore/Trait/Netsuite/ItemFulfillment.php, _underscore/Trait/Netsuite/SalesOrder.php, _underscore/Component/Library/NetSuite/NetSuite.php, _underscore/Model/Client/TrackingNumber.php, _underscore/Model/Client/ShippingMethod.php, _underscore/Model.php, _underscore/Cloud.php |
|
|
9
9
|
| [Client Email Template Sending](features/email-template-sending.md) | `_Model_Client_EmailTemplate` sends a stored, client-defined email template by UUID. | _underscore/Model/Client/EmailTemplate.php, _underscore/Model/Client/EmailTemplateOutgoingEmailAddress.php, _underscore/Email.php |
|
|
10
10
|
| [Record-Changed Event Publishing (_Event::publish to SQS)](features/event-publish-sqs.md) | `_Event::publish()` (in `_underscore/Event.php`) is the PHP side of the real-time event pipeline. | _underscore/Event.php |
|
|
11
11
|
| [Forecast.Sales NetSuite import engine (real-time webhook)](features/forecast-sale-import.md) | Real-time importer that takes a NetSuite **sale** record and writes its lines into `Forecast.Sales` (the Forecast2 revenue table). | _underscore/Component/Forecast/SaleImport/SaleImport.php, _underscore/Component/Forecast/Db/Db.php, _underscore/Component/Api/Netsuite/Netsuite.php, worker2/Worker/Netsuite/Invoice.php, worker2/Worker/Netsuite/CashSale.php, worker2/Worker/Netsuite/CreditMemo.php, worker2/Worker/Netsuite/CashRefund.php, worker2/Worker/Netsuite/JournalEntry.php, worker2/Worker/Netsuite/Opportunity.php, worker2/Worker/Netsuite/SalesOrder.php, dbchanges2/Forecast/2026-06-26a - Add journalEntry to Sales transaction type enum.sql, test/@dave/test_invoice_lifecycle.php, test/@dave/test_je_lifecycle.php, test/@dave/test_creditmemo_lifecycle.php, test/@dave/test_cashsale_lifecycle.php, test/@dave/test_cashrefund_lifecycle.php, test/@dave/test_fetchrecord_routes.php, test/@dave/verify_je_classification.php, test/@dave/probe_je_accounts.php, test/@dave/probe_je_shape.php, test/@dave/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/NetSuite/api-message-queue/dev_ue_api_msg_queue_enqueue.js |
|
|
@@ -6,8 +6,8 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: architecture
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["jcardinal", "rgirish"]
|
|
9
|
+
updated: 2026-07-02
|
|
10
|
+
owners: ["jcardinal", "rgirish", "mhammontree"]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/_underscore.php
|
|
13
13
|
- _underscore/Loader.php
|
|
@@ -35,6 +35,9 @@ can be silently dropped — confirm commit semantics before relying on a write.
|
|
|
35
35
|
The Surface presentation layer replaces `Page::meta()` but `meta()` must NOT be deleted until
|
|
36
36
|
every page is cut over (per-surface, with a parity diff); config describes, PHP decides —
|
|
37
37
|
business logic never moves into Surface config.
|
|
38
|
+
Storage fields (`FIELD_STORAGE`) must be written by assigning then `save()` — historically a
|
|
39
|
+
`__get` refetch on the write path silently dropped the assigned value (fixed 2026-07); still avoid
|
|
40
|
+
*reading* a storage field between assign and save (it refetches and discards the unsaved value).
|
|
38
41
|
|
|
39
42
|
## Entry point & boot sequence
|
|
40
43
|
|
|
@@ -319,9 +322,19 @@ multi-file UI components (`.php`/`.html`/`.css`/`.js`) invoked as `<_ComponentNa
|
|
|
319
322
|
|
|
320
323
|
## Gotchas / known issues
|
|
321
324
|
|
|
325
|
+
- **`FIELD_STORAGE` writes were silently dropped platform-wide (fixed 2026-07, `Model.php`).** Two
|
|
326
|
+
bugs: (a) `buildSqlFieldValue()` read the field via `$this->$field`, which for a storage field
|
|
327
|
+
triggers `__get()` → a disk/S3 **refetch** that overwrote the just-assigned value before `save()`
|
|
328
|
+
persisted it — so storage-field writes never persisted, on both S3 and folder backends; fixed by
|
|
329
|
+
reading the raw `_model_fields[$field]['value']` for storage fields. (b) The folder-mode READ
|
|
330
|
+
branch checked `filesize($cacheFilePath)` (an S3-only temp path) instead of `filesize($filePath)`,
|
|
331
|
+
so folder reads always returned null; fixed. **Remaining follow-up (separate ticket):** `__get()`
|
|
332
|
+
still refetches storage unconditionally, so a dirty read (read-after-assign, pre-save) discards the
|
|
333
|
+
unsaved value.
|
|
322
334
|
- **`_Database::register()` auto-starts a lazy transaction (since Apr 2 2026, commit `fa7835ed`).** Any code that calls `register()` and then writes to that DB must call `_Database::transactionCommit()` before the request ends — otherwise MySQL silently rolls back all writes when the connection closes. Lazy transactions only materialise on the first write, so read-only callers are unaffected. See `_underscore/Database.php:48`. First discovered when Rate SAML user provisioning silently discarded all new user INSERTs (Jun 2026).
|
|
323
335
|
|
|
324
336
|
## Change history
|
|
325
337
|
- 2026-06-11 — Documented lazy transaction gotcha in `_Database::register()` (rgirish)
|
|
326
338
|
- 2026-06-25 — Added the Surface platform UI presentation/configuration layer (DB-driven UI config replacing `Page::meta()`, CTO-reviewed AGREE-WITH-ADJUSTMENTS) (jcardinal)
|
|
327
339
|
- 2026-06-29 — Surface made the enforced (un-flagged) presentation path; reserved id blocks renumbered to Records 333–341 / RecordFields 2246–2433 (known seed-vs-provisioned drift). (jcardinal)
|
|
340
|
+
- 2026-07-02 — Fixed framework-wide `FIELD_STORAGE` write-drop and folder-read bugs in `Model.php`; noted the remaining unconditional-refetch dirty-read follow-up. (mhammontree)
|
|
@@ -6,8 +6,8 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["jcardinal"]
|
|
9
|
+
updated: 2026-07-02
|
|
10
|
+
owners: ["jcardinal", "mhammontree"]
|
|
11
11
|
files:
|
|
12
12
|
- api2/Component/Api/V2/V2.php
|
|
13
13
|
- _underscore/Model/Core/Page.php
|
|
@@ -90,6 +90,28 @@ then checks the caller's roles against `AclRecordPermissions` (→ `EZ-1` if no
|
|
|
90
90
|
readable/writable fields from `AclFieldPermissions`. `_Model_Core_Page::meta()` reads the same
|
|
91
91
|
tables to return per-page ACL to the frontend.
|
|
92
92
|
|
|
93
|
+
## Record-script dispatch grant (`AclRecordScripts`) — a separate gate
|
|
94
|
+
|
|
95
|
+
A **scripted API** (Record Script) has its own ACL gate, **distinct from the four-table record-CRUD
|
|
96
|
+
chain above**: dispatch is authorized by an **`AclRecordScripts`** row, not by
|
|
97
|
+
`AclRecordPermissions`. Without it the call **403s** even though the route and the PHP method exist.
|
|
98
|
+
|
|
99
|
+
To expose a scripted API on a `_Model` in 2.0:
|
|
100
|
+
1. **Route** — a `Core.RecordScripts` row (id-agnostic; resolve the record by `route=`, not a
|
|
101
|
+
hardcoded id — same discipline as the reserved-id-block work). Fields: `recordId`, `method`
|
|
102
|
+
(GET/POST/…), `route` (the segment after the record name), `phpMethod`.
|
|
103
|
+
2. **Dispatch grant** — a Client-DB **`AclRecordScripts`** row for the roles that may call it
|
|
104
|
+
(typically Public / SuperUser / Base). This is the 403-preventing gate and is the step most
|
|
105
|
+
easily missed; it mirrors the **Surface `meta`** precedent (`GET /v2/surfaces/meta`).
|
|
106
|
+
3. **PHP method** — signature `public static function <phpMethod>(&$api, …namedQueryArgs)`; the
|
|
107
|
+
named args are parsed from the query string; the return value is JSON-encoded into the
|
|
108
|
+
response `data`. Tenant scope comes from the JWT-pinned client DB — no manual client filtering.
|
|
109
|
+
|
|
110
|
+
Worked example: the reprint API `GET /v2/tracking-numbers/reprint`
|
|
111
|
+
(`_Model_Client_TrackingNumber::reprintLabelsApi`) required both a `Core.RecordScripts` route and a
|
|
112
|
+
Client-DB `AclRecordScripts` grant (migrations in `dbchanges2`) before it stopped 403ing — see the
|
|
113
|
+
carrier-shipping-labels feature doc.
|
|
114
|
+
|
|
93
115
|
## Checklist (so the chain is never half-built)
|
|
94
116
|
|
|
95
117
|
- [ ] `AclRecordPermissions` row for the role × record with the right CRUD flags.
|
|
@@ -101,6 +123,10 @@ tables to return per-page ACL to the frontend.
|
|
|
101
123
|
|
|
102
124
|
## Change history
|
|
103
125
|
|
|
126
|
+
- **2026-07-02** — Added the record-script dispatch grant (`AclRecordScripts`) as a gate distinct
|
|
127
|
+
from the record-CRUD chain, with the full recipe for exposing a scripted API on a `_Model`
|
|
128
|
+
(Core.RecordScripts route + Client `AclRecordScripts` grant + `(&$api, …)` method), discovered
|
|
129
|
+
wiring the `tracking-numbers/reprint` scripted API. (mhammontree)
|
|
104
130
|
- **2026-06-23** — Documented after repeatedly missing the logic-group/expression rows when
|
|
105
131
|
granting access to new records. Created alongside the `item-translations` record (331) ACL,
|
|
106
132
|
whose grant in `dbchanges2/Client/2026-06-23b - ItemTranslationsAcl.sql` is a worked example of
|
|
@@ -6,12 +6,14 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-07-02
|
|
10
10
|
owners: [mhammontree]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Model/Client/ItemFulfillment.php
|
|
13
|
+
- _underscore/Model/Client/TrackingNumber.php
|
|
13
14
|
- _underscore/Model/Client/ItemFulfillments/TrackingNumber.php
|
|
14
15
|
- _underscore/Component/Library/LabelPdf/LabelPdf.php
|
|
16
|
+
- _underscore/Component/Library/Carriers/ShipmentRequest/ShipmentRequest.php
|
|
15
17
|
- _underscore/Component/Library/Carriers/Ups/Ups.php
|
|
16
18
|
- _underscore/Component/Library/Carriers/Fedex/Fedex.php
|
|
17
19
|
- _underscore/Trait/Netsuite/ItemFulfillment.php
|
|
@@ -33,7 +35,11 @@ Backend mechanics behind TOGa Supply's Fulfill & Ship: buying a carrier label
|
|
|
33
35
|
(UPS/FedEx), persisting it, and creating the NetSuite Item Fulfillment with tracking
|
|
34
36
|
number and label attached. As of 2026-06 the label is requested + stored as a raw
|
|
35
37
|
**PNG** and the printable PDF is **generated on demand** via `_Component_Library_LabelPdf`
|
|
36
|
-
(FPDF) — superseding the old Labelary ZPL→PDF approach.
|
|
38
|
+
(FPDF) — superseding the old Labelary ZPL→PDF approach. As of 2026-07 the **reprint** combine
|
|
39
|
+
also runs on the backend — a scripted API (`reprintLabelsApi`) reads the stored PNGs and returns
|
|
40
|
+
one combined base64 PDF (the client-side `pdf-lib` merge is gone); **return labels** are generated
|
|
41
|
+
per carrier (`generateReturnLabel`); and carrier **billing account** is threaded from the tracking
|
|
42
|
+
record instead of a hardcoded constant.
|
|
37
43
|
|
|
38
44
|
## Key files / entry points
|
|
39
45
|
|
|
@@ -51,6 +57,20 @@ number and label attached. As of 2026-06 the label is requested + stored as a ra
|
|
|
51
57
|
(`Component/Library/LabelPdf/LabelPdf.php`): builds a multi-page 4x6 PDF from raw PNG label
|
|
52
58
|
images (one per page, optional "Return Label" caption) via FPDF; returns the PDF as a binary
|
|
53
59
|
string. Built to take N pages so a combined shipping + return label is one print-once PDF.
|
|
60
|
+
- `_Model_Client_TrackingNumber::reprintLabelsApi(&$api, $uuids, $caption)`
|
|
61
|
+
(`Model/Client/TrackingNumber.php`): the **backend reprint** scripted API, exposed as a
|
|
62
|
+
`Core.RecordScripts` route **`GET /v2/tracking-numbers/reprint`**. Loads each `TrackingNumbers`
|
|
63
|
+
record by UUID, reads its stored raw PNG (`labelPdfFile`), and returns a **combined base64 PDF**
|
|
64
|
+
via `buildFromPngLabels`. Replaced the frontend `pdf-lib` merge (which broke once labels became
|
|
65
|
+
PNG — it expected a base64 PDF). Requires a Client-DB `AclRecordScripts` dispatch grant or it
|
|
66
|
+
403s — see the ACL permission-chain doc's record-script dispatch section.
|
|
67
|
+
- `_Model_Client_ItemFulfillment::generateReturnLabel(&$api, $uuid)`
|
|
68
|
+
(`Model/Client/ItemFulfillment.php`): builds a per-carrier **return** shipment request
|
|
69
|
+
(ship-from = customer, ship-to = warehouse return address; Ground service; FedEx uses drop-off
|
|
70
|
+
pickup), creates a **second** `TrackingNumbers` record, stores its raw PNG, and links it as
|
|
71
|
+
`returnTrackingNumberId` on the `ItemFulfillments_TrackingNumbers` header bridge.
|
|
72
|
+
- `getLabelFormat()` (client UPS config helper): now **defaults to `PNG`** (was ZPL) and validates
|
|
73
|
+
against a supported-format allowlist, so a carrier label reliably comes back as an embeddable PNG.
|
|
54
74
|
- `_Component_Library_Carriers_Ups::submitShipmentRequest`
|
|
55
75
|
(`Component/Library/Carriers/Ups/Ups.php`): returns `{ success, errorMessage,
|
|
56
76
|
trackingNumber, encodedLabel }`.
|
|
@@ -70,8 +90,8 @@ number and label attached. As of 2026-06 the label is requested + stored as a ra
|
|
|
70
90
|
The label is **stored as the raw carrier PNG** (not a Labelary PDF) and the printable PDF is
|
|
71
91
|
**generated on demand** server-side:
|
|
72
92
|
|
|
73
|
-
1. **Carrier returns PNG.** UPS via `
|
|
74
|
-
|
|
93
|
+
1. **Carrier returns PNG.** UPS via `getLabelFormat()`, which **now defaults to `PNG`** (was ZPL)
|
|
94
|
+
and validates against a supported-format allowlist; FedEx via `$shipmentRequest->labelImageType =
|
|
75
95
|
'PNG'` + `labelStockType = 'PAPER_4X6'`. Labelary is gone.
|
|
76
96
|
2. **Store the raw PNG** on `TrackingNumbers.labelPdfFile` (a `FIELD_STORAGE` field; the name is
|
|
77
97
|
retained though it now holds a PNG) via the model/S3 mechanism — never touch S3 by hand.
|
|
@@ -80,9 +100,28 @@ The label is **stored as the raw carrier PNG** (not a Labelary PDF) and the prin
|
|
|
80
100
|
display + the NetSuite File Cabinet copy.
|
|
81
101
|
|
|
82
102
|
Jeff approved **PNG over GIF**: a PDF lib embeds PNG with **no extra extension** (FPDF parses
|
|
83
|
-
non-alpha PNG in pure PHP — no GD/Imagick).
|
|
84
|
-
|
|
85
|
-
the
|
|
103
|
+
non-alpha PNG in pure PHP — no GD/Imagick). The remaining wiring is now done (2026-07): the
|
|
104
|
+
reprint combine moved to the backend `reprintLabelsApi` scripted API and return-label generation
|
|
105
|
+
(`generateReturnLabel`) is implemented — see the sections below.
|
|
106
|
+
|
|
107
|
+
## Carrier billing account & return labels (implemented 2026-07)
|
|
108
|
+
|
|
109
|
+
**Billing account is data-driven, not a constant.** The selected
|
|
110
|
+
`shippingCarrierAccountNumber` on the tracking record is threaded into the carrier request as
|
|
111
|
+
`ShipmentRequest->$shipperAccountNumber` for both UPS and FedEx billing. The old hardcoded
|
|
112
|
+
`SHIPPER_NUMBER` / `ACCOUNT_NUMBER` constants were **removed**. Two guards:
|
|
113
|
+
- **Fail fast on an empty account** — never ship on a blank billing account.
|
|
114
|
+
- **Fail if an Item Fulfillment has more than one tracking number** — with >1 tracking record the
|
|
115
|
+
code can't decide which account to bill, so it errors rather than guess.
|
|
116
|
+
|
|
117
|
+
**Return labels.** When `ShipmentRequest->isReturn` is set, the carrier request injects the
|
|
118
|
+
return-service flags: UPS `Shipment.ReturnService.Code = '9'` (Print Return Label); FedEx
|
|
119
|
+
`shipmentSpecialServices.returnShipmentDetail` (`returnType = PRINT_RETURN_LABEL`, RMA reason
|
|
120
|
+
"Customer Return"). `generateReturnLabel` builds the return request with **ship-from = customer,
|
|
121
|
+
ship-to = warehouse**, at **Ground / customer-drops-off** service level (decision), creates a 2nd
|
|
122
|
+
`TrackingNumbers` record, stores its raw PNG, and links `returnTrackingNumberId` on the
|
|
123
|
+
`ItemFulfillments_TrackingNumbers` bridge. Warehouse return addresses come from a `RETURN_ADDRESSES`
|
|
124
|
+
set (two verified addresses: Naperville default + Plainview).
|
|
86
125
|
|
|
87
126
|
## `FIELD_STORAGE` mechanics (reference)
|
|
88
127
|
|
|
@@ -101,14 +140,34 @@ store whatever value was assigned. `_Cloud::copyFileToS3` **re-throws** on AWS e
|
|
|
101
140
|
if no exception reached the caller and execution continued, the write was never
|
|
102
141
|
attempted.
|
|
103
142
|
|
|
143
|
+
**Root cause of the "labelPdfFile never lands" bug — two framework-core `Model.php` bugs
|
|
144
|
+
(fixed 2026-07):** these were the true reason storage-field writes silently vanished, and they
|
|
145
|
+
affect **every** 2.0 model that uses `FIELD_STORAGE`, not just labels — so a fuller
|
|
146
|
+
framework-level note is pending on the `_underscore` architecture doc.
|
|
147
|
+
1. **Write path re-fetched over the assigned value.** `buildSqlFieldValue()` read the field via
|
|
148
|
+
`$this->$field`, which for a `FIELD_STORAGE` field triggers `__get()` → a disk/S3 **refetch**
|
|
149
|
+
that overwrote the just-assigned in-memory value *before* `save()` persisted it. Net effect:
|
|
150
|
+
storage-field writes silently never persisted (both S3 and folder backends). Fixed by reading
|
|
151
|
+
the raw `_model_fields[$field]['value']` for storage fields instead of going through `__get`.
|
|
152
|
+
2. **Folder-mode read used the wrong path for its size check.** The folder-storage READ branch
|
|
153
|
+
checked `filesize($cacheFilePath)` (an S3-only temp path) instead of `filesize($filePath)`, so
|
|
154
|
+
folder-mode reads always returned null. Fixed to check `$filePath`.
|
|
155
|
+
|
|
156
|
+
**Known remaining follow-up (separate ticket):** `__get()` still refetches storage
|
|
157
|
+
**unconditionally**, so a *dirty read* (reading a storage field after assigning but before saving)
|
|
158
|
+
still discards the unsaved in-memory value.
|
|
159
|
+
|
|
104
160
|
## Gotchas / known issues
|
|
105
161
|
|
|
106
162
|
- UPS **test** endpoint (`wwwcie.ups.com`, debug mode) returns a constant tracking
|
|
107
163
|
number `1ZXXXXXXXXXXXXXXXX` + SAMPLE labels → `TrackingNumbers.number` UNIQUE
|
|
108
164
|
collision and File Cabinet duplicate-filename failures on repeat tests (see the
|
|
109
165
|
fulfill-and-ship doc for the cleanup SQL). Never in prod.
|
|
110
|
-
-
|
|
111
|
-
|
|
166
|
+
- ~~`upsShipmentApi` reads ship-to from the Sales Order, not the Item Fulfillment.~~
|
|
167
|
+
**Fixed 2026-07:** carrier label ship-to now uses
|
|
168
|
+
`COALESCE(ItemFulfillments.shipToAddressId, SalesOrders.shipToAddressId)` — the edited IF address,
|
|
169
|
+
falling back to the SO — aligning the label with `fulfill()`'s NetSuite query. Form-edited
|
|
170
|
+
addresses now reach the carrier.
|
|
112
171
|
- UPS `AddressLine` max 35 chars/line; send an array of trimmed lines (FedEx already
|
|
113
172
|
does), not `line1 . ' ' . line2`.
|
|
114
173
|
- `ShippingMethods.code` is carrier-scoped: UPS numeric (`03` Ground, `01` Next Day
|
|
@@ -142,19 +201,33 @@ attempted.
|
|
|
142
201
|
works with the no-throw second arg: `_Config::fedex('tracking_client_id', false) ?: …` (same as
|
|
143
202
|
the UPS client). Without it, FedEx shipping dies with "Configuration parameter
|
|
144
203
|
'tracking_client_id' … has not been defined".
|
|
145
|
-
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
204
|
+
- ~~`labelPdfFile` (`FIELD_STORAGE`) write silently skips / S3 label save not landing.~~
|
|
205
|
+
**Root cause found + fixed 2026-07** — it was two framework-core `Model.php` `FIELD_STORAGE`
|
|
206
|
+
bugs (a `__get` refetch overwriting the assigned value on the write path, and a wrong-path
|
|
207
|
+
`filesize` check on the folder-mode read), not a lazy-load issue. See the *FIELD_STORAGE
|
|
208
|
+
mechanics* section above.
|
|
209
|
+
- **`ItemFulfillment.php` had two SQL injections + debug output (fixed 2026-07).** `postPost()`
|
|
210
|
+
interpolated `$payload->uuid` and `fulfill()` interpolated the NetSuite `$internalId` into an
|
|
211
|
+
UPDATE — both now escaped (`_Database::escape`). `fulfill()` also had leftover debug
|
|
212
|
+
`echo`/`print_r` that leaked PII and could corrupt the JSON API envelope — removed. Never emit raw
|
|
213
|
+
output from a scripted API/interceptor; it breaks the `{isSuccess,…,data}` response.
|
|
150
214
|
- **FPDF can't embed alpha-channel or interlaced PNG** — carrier label PNGs must be flat raster.
|
|
151
215
|
If one isn't, `buildFromPngLabels` throws; fix = GD normalize (flatten/de-interlace) or TCPDF.
|
|
152
216
|
- **UPS shipper number must match the endpoint.** Beta hits the UPS **CIE test** endpoint
|
|
153
217
|
(`wwwcie.ups.com`); a production account number there returns `120100`/`120121`
|
|
154
218
|
("missing/invalid shipper number" / "cannot be used"). Use a CIE-enabled shipper number for
|
|
155
|
-
test, or point at prod UPS.
|
|
219
|
+
test, or point at prod UPS. **The shipper/billing account is now the tracking record's
|
|
220
|
+
`shippingCarrierAccountNumber`** (threaded via `ShipmentRequest->$shipperAccountNumber`); the
|
|
221
|
+
hardcoded `SHIPPER_NUMBER`/`ACCOUNT_NUMBER` constants were removed. So for CIE tests the account
|
|
222
|
+
on the tracking record itself must be CIE-enabled.
|
|
223
|
+
- **FedEx credentials are hardcoded in `Fedex.php` and were intentionally retained** (Mark's
|
|
224
|
+
decision) — do not "fix" them into config as part of unrelated work.
|
|
225
|
+
- **Ship guards (2026-07).** Shipping now **fails fast** if the selected carrier account is empty,
|
|
226
|
+
and **errors if an Item Fulfillment has >1 tracking number** (can't determine which account to
|
|
227
|
+
bill) — expect a hard error rather than a silently-wrong bill in those cases.
|
|
156
228
|
|
|
157
229
|
## Change history
|
|
230
|
+
- 2026-07-02 — Reprint combine moved to the backend: new `_Model_Client_TrackingNumber::reprintLabelsApi` scripted API (RecordScript `GET /v2/tracking-numbers/reprint`) returns a combined base64 PDF from the stored PNGs (frontend `pdf-lib` merge removed). Added return-label generation (`generateReturnLabel`; UPS ReturnService `9` / FedEx `PRINT_RETURN_LABEL`; Ground/customer-drops-off; `RETURN_ADDRESSES`; links `returnTrackingNumberId`). Carrier billing account threaded from the tracking record (`shippingCarrierAccountNumber`) — removed `SHIPPER_NUMBER`/`ACCOUNT_NUMBER` constants, fail-fast on empty account, error on >1 tracking number per IF. Ship-to now `COALESCE(IF, SO)` address. `getLabelFormat()` defaults PNG + allowlist. Fixed two SQL injections (`postPost` `$payload->uuid`, `fulfill` NetSuite `$internalId`) and removed leftover debug `echo`/`print_r` from `fulfill()`. Found + fixed the framework-core root cause of storage writes silently vanishing (two `Model.php` `FIELD_STORAGE` bugs — see FIELD_STORAGE mechanics). (mhammontree)
|
|
158
231
|
- 2026-06-18 — Implemented the label-storage rework: request PNG from UPS/FedEx, store the raw PNG, generate the printable PDF on demand via the new `_Component_Library_LabelPdf` (FPDF); dropped Labelary. Added the FedEx tracking-cred config-fallback fix and the `_Loader`→Composer FPDF autoloader toggle. Open: the `labelPdfFile` S3 write still skips on a lazy-model save. (mhammontree)
|
|
159
232
|
- 2026-06-16 — Repointed the carrier-label/NS-IF tracking-number queries (`upsShipmentApi`, `fedexShipmentApi`, `fulfill`, `createNetsuiteItemFulfillment`) from the dropped `ItemFulfillmentPackages` table to the `ItemFulfillments_TrackingNumbers` bridge — they had been saving the label/`number` to the wrong/null record (reprint `labelPdfFile` null; NS IF missing tracking). Confirmed working on beta. (mhammontree)
|
|
160
233
|
- 2026-06-10 — Documented carrier shipping label mechanics (UPS/FedEx) + the authoritative GIF label-storage decision and NetSuite IF attachment. (mhammontree)
|
|
@@ -6,7 +6,7 @@ project: TOGa Supply
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: architecture
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-07-02
|
|
10
10
|
owners: [mhammontree]
|
|
11
11
|
files:
|
|
12
12
|
- toga2-supply/src/api/toga.ts
|
|
@@ -28,6 +28,11 @@ Fulfillment creation, label printing/reprinting). It is a pure frontend: all bus
|
|
|
28
28
|
logic lives in the 2.0 API (`api2` engine over `_underscore` models). It has **no
|
|
29
29
|
backend code of its own** — `dependsOn: [api2]`.
|
|
30
30
|
|
|
31
|
+
**Critical rules:** `toga2-supply` is a **legacy app slated for replacement** — build UI
|
|
32
|
+
**in-repo** and do **not** extract shared components to `@agilant/toga-blox` (that investment
|
|
33
|
+
belongs in the successor app). It is a pure frontend (`dependsOn: [api2]`); all business logic
|
|
34
|
+
stays in the 2.0 API, never in the frontend.
|
|
35
|
+
|
|
31
36
|
## Topology (dev/test setup)
|
|
32
37
|
|
|
33
38
|
- The frontend runs **locally** on the developer machine (e.g.
|
|
@@ -66,6 +71,8 @@ Response envelope (2.0 API): `{ isSuccess, status, error, messages, data: { <rou
|
|
|
66
71
|
- Browser print: the print dialog handles one file at a time and `window.print()` fires
|
|
67
72
|
once per session — hence the combined-PDF reprint design. Popups must be allowed
|
|
68
73
|
(label window is `window.open`).
|
|
74
|
+
- Legacy status: this repo is being replaced; do not refactor toward `toga-blox` extraction —
|
|
75
|
+
keep new UI local to the repo.
|
|
69
76
|
|
|
70
77
|
## Related docs
|
|
71
78
|
|
|
@@ -6,7 +6,7 @@ project: TOGa Supply
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-07-02
|
|
10
10
|
owners: [mhammontree]
|
|
11
11
|
files:
|
|
12
12
|
- toga2-supply/src/pages/ShipmentItems/view/ShipmentItemsPage.tsx
|
|
@@ -59,18 +59,19 @@ GroWrk, June 2026); FedEx + UPS both need verification before prod.
|
|
|
59
59
|
`saveShipmentToNetsuite` → `createNetsuiteItemFulfillment`, which attaches the label
|
|
60
60
|
to the IF **after** creating it (the IF doesn't exist yet when the label is bought).
|
|
61
61
|
|
|
62
|
-
## Reprint (
|
|
62
|
+
## Reprint (backend-combined, 2026-07)
|
|
63
63
|
|
|
64
64
|
Fulfilled-shipments view (`FulfilledShipmentsPage` → `ShipmentsCardTableForm` with
|
|
65
|
-
`isFulfilledShipmentsView`): multi-select shipments,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
`
|
|
65
|
+
`isFulfilledShipmentsView`): multi-select shipments, print one combined multi-page PDF once.
|
|
66
|
+
Because labels are now stored as raw carrier **PNG** on `TrackingNumbers.labelPdfFile` (see the
|
|
67
|
+
carrier-shipping-labels doc), the **combine now runs on the backend**: `ShipmentsApi.ts` calls the
|
|
68
|
+
scripted API **`GET /v2/tracking-numbers/reprint`** (`_Model_Client_TrackingNumber::reprintLabelsApi`,
|
|
69
|
+
passing the selected tracking UUIDs + caption), which reads the stored PNGs and returns one combined
|
|
70
|
+
base64 **PDF**. The old client-side `pdf-lib` merge (which expected a base64 PDF and broke on a PNG)
|
|
71
|
+
was **removed**. The tracking source was also corrected from `shipment.itemFulfillmentPackages` to
|
|
72
|
+
`itemFulfillmentTrackingNumbers` (bridge migration). Reading tracking still needs the bridge ACL
|
|
73
|
+
chain (see gotchas) **and** the reprint RecordScript needs its `AclRecordScripts` dispatch grant, or
|
|
74
|
+
the call 403s and returns no label.
|
|
74
75
|
|
|
75
76
|
## Gotchas / known issues
|
|
76
77
|
|
|
@@ -81,9 +82,10 @@ The reprint reference was also corrected from `shipment.itemFulfillmentPackages`
|
|
|
81
82
|
both between runs:
|
|
82
83
|
`UPDATE TrackingNumbers SET number = CONCAT(number,'-',id) WHERE number='1ZXXXXXXXXXXXXXXXX'`
|
|
83
84
|
and delete/rename the prior File Cabinet file. Never happens in prod.
|
|
84
|
-
-
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
- ~~Address source mismatch: ship-to read from the SO, not the edited IF address.~~
|
|
86
|
+
**Fixed 2026-07:** carrier ship-to now uses `COALESCE(ItemFulfillments.shipToAddressId,
|
|
87
|
+
SalesOrders.shipToAddressId)` — the form-edited IF address, falling back to the SO — so
|
|
88
|
+
address edits now reach the carrier.
|
|
87
89
|
- **UPS address limits**: `AddressLine` max 35 chars/line; code currently sends
|
|
88
90
|
`line1 + ' ' + line2` as one line — should send a trimmed array (FedEx path already
|
|
89
91
|
does).
|
|
@@ -139,16 +141,15 @@ The reprint reference was also corrected from `shipment.itemFulfillmentPackages`
|
|
|
139
141
|
|
|
140
142
|
## Remaining work
|
|
141
143
|
|
|
142
|
-
- Reprint rewire
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
combined PDF.
|
|
149
|
-
-
|
|
150
|
-
|
|
151
|
-
verification before prod.
|
|
144
|
+
- ~~Reprint rewire~~ — done (backend `reprintLabelsApi` combine; see Reprint section).
|
|
145
|
+
- ~~Address-source decision~~ — done (`COALESCE(IF, SO)`).
|
|
146
|
+
- Return-label flow: backend generation is done (`generateReturnLabel`; UPS ReturnService `9` /
|
|
147
|
+
FedEx `PRINT_RETURN_LABEL`; 2nd tracking number linked via `returnTrackingNumberId` on the
|
|
148
|
+
bridge; Ground/customer-drops-off). Remaining **frontend** polish: "needs return label" control,
|
|
149
|
+
a return-address editable combo ("Rolodex pattern" of client locations, overridable, validated),
|
|
150
|
+
and "return label" text on return pages of the combined PDF.
|
|
151
|
+
- UPS 35-char hardening, location-scoped inventory lookup, carrier/method on the NS IF,
|
|
152
|
+
responsiveness per Figma, FedEx + UPS end-to-end verification before prod.
|
|
152
153
|
|
|
153
154
|
## Client variations
|
|
154
155
|
|
|
@@ -158,6 +159,7 @@ not the base `_Model_Client_ItemFulfillment`. Tested with GroWrk; UPS support wa
|
|
|
158
159
|
for Compass and is not yet in prod.
|
|
159
160
|
|
|
160
161
|
## Change history
|
|
162
|
+
- 2026-07-02 — Reprint rewired to the backend: `ShipmentsApi.ts` now calls `GET /v2/tracking-numbers/reprint` (`reprintLabelsApi`) for a combined base64 PDF built from the stored PNGs; removed the stale client-side `pdf-lib` merge. Return-label backend generation landed (`generateReturnLabel`, `returnTrackingNumberId` on the bridge). Ship-to address source resolved to `COALESCE(IF, SO)`. (mhammontree)
|
|
161
163
|
- 2026-06-18 — PNG label-storage rework reflected on the frontend: labels now store as PNG and the PDF is built on the backend (`LabelPdf`), so the client-side `pdf-lib` reprint is stale and must be rewired to a backend generate endpoint. Fixed the Fulfilled Shipments responsiveness/clip bug (Reprint button unreachable at 100% zoom) — `overflow-scroll` → `overflow-auto` and gave `FulfilledShipmentsPage`'s outer its own scroll; documented the `/shipments` vs `/fulfilled-shipments` two-component trap and the `AuthLayout overflow-hidden` clip. (mhammontree)
|
|
162
164
|
- 2026-06-16 — Drove the full beta flow green for UPS/GroWrk. Updated the save step to the `/item-fulfillment-tracking-numbers` bridge (FE commit `b5c16592d`, key `itemFulfillmentTrackingNumbers`). Added gotchas: local-FE↔deployed-beta-BE skew, `dtSubmitted` fulfilled/pending gating, `createNetsuiteItemFulfillment` orderLine fragility on partial/already-fulfilled orders, and the bridge-ACL 403 that nulls tracking/labels (reprint). (mhammontree)
|
|
163
165
|
- 2026-06-10 — Documented the Fulfill & Ship flow (SO sync, label purchase, NetSuite IF creation, success gating, reprint). Driven green on beta for UPS/GroWrk; FedEx + prod verification pending. (mhammontree)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 2.0 Backend Testing (no PHPUnit — ad-hoc client-pinned scripts)
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
project: _Underscore
|
|
5
|
+
client: shared
|
|
6
|
+
type: standard
|
|
7
|
+
status: active
|
|
8
|
+
updated: 2026-07-02
|
|
9
|
+
owners: [mhammontree]
|
|
10
|
+
files: []
|
|
11
|
+
related:
|
|
12
|
+
- ../apps/_underscore/architecture.md
|
|
13
|
+
- ../apps/_underscore/features/per-client-database-connections.md
|
|
14
|
+
- ../apps/test/architecture.md
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Summary
|
|
18
|
+
|
|
19
|
+
The 2.0 backend (`_underscore` + its apps) has **no PHPUnit harness**. Verify backend behavior
|
|
20
|
+
with **ad-hoc scripts in the `test` repo** (per-dev folders, e.g. `@Mark`) that bootstrap
|
|
21
|
+
`_underscore` from a 2.0 app and pin a real client database explicitly. This is the accepted
|
|
22
|
+
verification path until/unless a formal harness exists — do not assume a `phpunit` suite.
|
|
23
|
+
|
|
24
|
+
## Bootstrapping a verification script
|
|
25
|
+
|
|
26
|
+
From a 2.0 app root (e.g. `worker2`): `chdir` into the app, require Composer autoload, require
|
|
27
|
+
`_underscore.php`, and set `ENVIRONMENT` before bootstrap:
|
|
28
|
+
|
|
29
|
+
```php
|
|
30
|
+
chdir('/path/to/worker2');
|
|
31
|
+
require 'vendor/autoload.php';
|
|
32
|
+
require '_underscore.php'; // boots Loader/Config/etc.
|
|
33
|
+
// ENVIRONMENT must be set (env var / server) so _Config picks the right INI
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## You MUST pin the client DB explicitly
|
|
37
|
+
|
|
38
|
+
`_underscore::DB_CLIENT` is a **logical alias**, not a schema name — there is no database literally
|
|
39
|
+
named `Client`. Real per-client schemas are `Client_True`, `Client_Aig`, etc. In a real request V2
|
|
40
|
+
pins the concrete schema from the JWT; a standalone script has no JWT, so **you must register it**:
|
|
41
|
+
|
|
42
|
+
```php
|
|
43
|
+
_Database::register(
|
|
44
|
+
database: 'Client_True', // real per-client schema
|
|
45
|
+
// …host/user/pw from config…
|
|
46
|
+
alias: _underscore::DB_CLIENT // the logical alias models resolve against
|
|
47
|
+
);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Without this, any `Model/Client/*` query resolves the unmapped alias and fails. Use a client whose
|
|
51
|
+
schema you have locally/on the target env (internal testing uses `Client_True` = TOGA Technology).
|
|
52
|
+
Remember the per-client **logs** connection trap too (see the per-client-database-connections doc).
|
|
53
|
+
|
|
54
|
+
## Rules
|
|
55
|
+
|
|
56
|
+
- Do not add or assume a PHPUnit suite for 2.0 backend work; write a `test`-repo script instead.
|
|
57
|
+
- Always pin the concrete `Client_<Id>` schema to `DB_CLIENT` (and logs/archive if the code logs).
|
|
58
|
+
- Keep scripts in your per-dev `test` folder; never point them at production databases.
|
package/package.json
CHANGED
package/skills/kickoff/SKILL.md
CHANGED
|
@@ -33,6 +33,52 @@ description: Start-of-session context loader for TOGA Technology projects. Run t
|
|
|
33
33
|
> block message is never to fight it: run Steps 0–3, and the moment preflight executes you
|
|
34
34
|
> are free to read docs and work. (Emergency override only: `KICKOFF_GATE_DISABLED=1`.)
|
|
35
35
|
|
|
36
|
+
## Session permission policy — auto-accept local file I/O, ALWAYS confirm dangerous execution
|
|
37
|
+
|
|
38
|
+
> **This policy is in force for the ENTIRE session, from the moment `/kickoff` runs until it
|
|
39
|
+
> ends. It is a standing, team-wide default — apply it without being reminded.**
|
|
40
|
+
>
|
|
41
|
+
> The intent: keep the developer in flow for safe, reversible, local work, while never letting
|
|
42
|
+
> an irreversible or environment-touching action happen without an explicit human "go ahead".
|
|
43
|
+
|
|
44
|
+
**Auto-accept (proceed without a permission prompt) — safe, local, reversible:**
|
|
45
|
+
|
|
46
|
+
- **Reading files** — `Read`, `Grep`, `Glob`, and any read-only inspection of the local
|
|
47
|
+
working tree. Never pause to ask "may I read this?".
|
|
48
|
+
- **Writing/editing files** in the local working tree — `Write`, `Edit`, `NotebookEdit`, and
|
|
49
|
+
creating new local files. Proceed as long as the target is inside the developer's local
|
|
50
|
+
checkout(s). (Standard write discipline still applies — see the stub-first rule in Step 6.)
|
|
51
|
+
|
|
52
|
+
**ALWAYS ask for explicit permission first (never auto-run) — anything that executes, leaves
|
|
53
|
+
the machine, or touches a shared/remote/production environment:**
|
|
54
|
+
|
|
55
|
+
- **Shell execution** — every `Bash` / PowerShell command runner invocation. Before running
|
|
56
|
+
one, state what it does and why, and wait for the developer's "go ahead". (The kickoff gate
|
|
57
|
+
already blocks shell during priming; this extends the caution to the whole session.)
|
|
58
|
+
- **Deploys, AWS / DevOps, and infrastructure** — `/ship`, `/land-and-deploy`, `/canary`, the
|
|
59
|
+
`devops` agent's AWS CLI/console actions, Elastic Beanstalk / EC2 / RDS / S3 / IAM changes,
|
|
60
|
+
and any command that mutates infrastructure. Confirm the target environment explicitly.
|
|
61
|
+
- **Remote database access** — the `toga-db` MCP tools (`toga_query`, etc.) and any query run
|
|
62
|
+
against **production or any non-local environment**. Name the cluster/environment and get a
|
|
63
|
+
yes before executing. Treat production as require-confirmation on every call.
|
|
64
|
+
- **Network, browser, and remote git** — `WebFetch`/`WebSearch`, the `/browse` browser
|
|
65
|
+
automation, `git push`, PR creation/merge, and any call that sends data off the machine or
|
|
66
|
+
writes to a remote. Confirm before each.
|
|
67
|
+
|
|
68
|
+
**Rules of application:**
|
|
69
|
+
|
|
70
|
+
1. When in doubt about whether an action executes something or reaches outside the local
|
|
71
|
+
environment, **treat it as dangerous and ask.** The default for the unknown is *confirm*.
|
|
72
|
+
2. "Auto-accept file I/O" does **not** mean "act without judgment" — still respect
|
|
73
|
+
`/freeze`, `/guard`, and any directory scoping the developer set.
|
|
74
|
+
3. A blanket "go ahead", "just do it", or "you have full access" from the developer covers the
|
|
75
|
+
current task's expected actions, but you must **still flag** any dangerous action that falls
|
|
76
|
+
outside the scope they approved (a different environment, a production write, a deploy).
|
|
77
|
+
4. This is a **directive, not an enforcement mechanism.** For hard, mechanical enforcement the
|
|
78
|
+
team should also encode these as `permissions` allow/ask rules in the checked-in
|
|
79
|
+
`.claude/settings.json` (allow `Read`/`Edit`/`Write`; `ask` for `Bash` and the remote/DB/
|
|
80
|
+
deploy tools). Until that exists, follow this policy by discipline on every session.
|
|
81
|
+
|
|
36
82
|
## Arguments — text passed after `/kickoff` never skips any step
|
|
37
83
|
|
|
38
84
|
Trailing text (e.g. `/kickoff worker2 backend fix for Compass`, even a detailed paragraph
|
|
@@ -253,6 +299,10 @@ Tell the developer concisely:
|
|
|
253
299
|
summaries" list — the developer must see that the core is loaded and which app pulled it in.
|
|
254
300
|
- Which knowledge docs you loaded (by title). Where a repo/standard has **no knowledge yet**,
|
|
255
301
|
say so explicitly: "No knowledge captured yet for X — `capture` will build it as you work."
|
|
302
|
+
- **State the session permission policy in one line** so the developer knows the rules for
|
|
303
|
+
this session: "File reads and local edits run automatically; anything that executes, deploys,
|
|
304
|
+
hits a remote/production environment, or leaves this machine, I'll confirm with you first."
|
|
305
|
+
(See *Session permission policy* near the top of this skill.)
|
|
256
306
|
- Confirm you're primed and ready for their first task.
|
|
257
307
|
|
|
258
308
|
**Heavy-load check (from preflight `estimate`).** Preflight returns
|