toga-ai 1.0.224 → 1.0.226
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/1.0/apps/worker/features/forecast2-netsuite-reconciliation.md +68 -1
- package/knowledge/2.0/apps/worker2/INDEX.md +1 -0
- package/knowledge/2.0/apps/worker2/features/clickup-github-autolink.md +44 -0
- package/knowledge/2.0/apps/worker2/workflows/ticket-to-pseudocode-planning.md +30 -1
- package/knowledge/INDEX.md +1 -1
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-29
|
|
10
10
|
owners: [dfranks]
|
|
11
11
|
files:
|
|
12
12
|
- test/@dave/checker.php
|
|
@@ -174,6 +174,22 @@ by reconciling a chosen tranDate range directly against NetSuite.
|
|
|
174
174
|
stays bounded under the existing 1G `memory_limit`. (This corrected a prior docblock claim that `$sqls`
|
|
175
175
|
was never accumulated globally.)
|
|
176
176
|
|
|
177
|
+
- **PROFIT is now compared for all THREE categories, not just Sales.** `checker.php` and `fixer.php` were
|
|
178
|
+
previously profit-aware only for Sales; they now compute and compare profit for **Open Orders** and
|
|
179
|
+
**Opportunities** too.
|
|
180
|
+
- **Open-order profit** mirrors fixer's per-line economics: `qtyOpen * (rate - unitCost)`,
|
|
181
|
+
`unitCost = costestimate/quantity` (divide-by-zero-safe), **round-then-sum per line** to match the stored
|
|
182
|
+
`decimal(14,2)` `OpenOrderItems.profit` column.
|
|
183
|
+
- **Opportunity profit** = the legacy importer's `Σ(amount − costEstimate)` over qualifying lines = SuiteQL
|
|
184
|
+
`Σ(-foreignamount + costestimate)`; **revenue** stays the header `projectedtotal` (pulled line-level via
|
|
185
|
+
`LEFT JOIN` + `MAX(projectedtotal)`). It **must EXCLUDE item-group parent lines** — SuiteQL
|
|
186
|
+
`itemtype = 'Group'` ↔ `App_Model_Forecast2_Item::NETSUITE_TRANSACTION_TYPE__ITEM_GROUP`, which the
|
|
187
|
+
importer skips. A dedicated `OPP_EXCLUDED_ITEMTYPES` constant (= the ORD exclusion list + `'Group'`)
|
|
188
|
+
carries this.
|
|
189
|
+
- **`fixer.fixOpportunities()` previously did NOT write `projectedProfit` at all.** It now computes
|
|
190
|
+
`projectedProfit` from the `listOpportunities()` itemList (`Σ(amount − costEstimate)`, skipping ITEM_GROUP
|
|
191
|
+
and items missing from the `Items` lookup, rounded to cents) so the FIX can actually resolve any
|
|
192
|
+
opportunity-profit drift the FIND flags (see the compute-identically invariant below).
|
|
177
193
|
- **checker → fixer handoff is WINDOW-based, not id-based (decision).** checker hands fixer a tight day
|
|
178
194
|
window and lets fixer **re-find** the discrepant ids inside it, rather than passing exact ids. Rationale:
|
|
179
195
|
maker≠checker independence (fixer re-derives the drift), and checker has already shrunk the window, so a
|
|
@@ -269,6 +285,40 @@ None — Forecast2 is a single shared dataset.
|
|
|
269
285
|
(NS 852.06 vs FC 852.07). **Correction to any prior note:** the open-orders **2025-04 "unlocalized month" was
|
|
270
286
|
this round-then-sum noise, NOT NS-live-vs-Forecast-replica timing jitter** — the rounding fix eliminated it,
|
|
271
287
|
disproving the earlier replica-jitter hypothesis.
|
|
288
|
+
- **NULL `rate` on cost-only open-order lines silently drops them from a NS profit SUM → permanent phantom
|
|
289
|
+
profit drift FIX can never resolve.** NetSuite open-order **cost-only** lines (zero revenue — e.g.
|
|
290
|
+
freight/handling sub-items) can carry a **NULL `rate`**. The FIND profit SQL computed `tl.rate - unitCost`;
|
|
291
|
+
in SQL `NULL - x = NULL`, so `ROUND(...)` returned NULL and the whole line **silently dropped out of
|
|
292
|
+
`SUM(...)`**, inflating the NS-side profit. But fixer's FIX (PHP) and the legacy importer treat a NULL rate
|
|
293
|
+
as **0** (`(float)$rate ?? 0`), so the stored Forecast2 profit was already correct. The two conventions
|
|
294
|
+
disagreed, so FIND kept flagging profit drift (e.g. Δprofit **$0.54** on many identical **$12.20** SOs;
|
|
295
|
+
large **$1,365–$9,500** deltas on **$0-revenue** SOs) that FIX could never close — every FIX reported
|
|
296
|
+
`ins=0/upd=0/del=0` because the line was already right. **Revenue never exposed it** (those lines are $0
|
|
297
|
+
revenue either way: NULL-dropped on NS vs 0-stored on FC both sum the same), so it surfaced **only in the
|
|
298
|
+
profit dimension**. **Fix:** wrap rate as `NVL(tl.rate, 0)` in the open-order **revenue AND profit**
|
|
299
|
+
expressions so FIND uses the same "NULL rate = 0" convention as FIX/importer. Applied in **both**
|
|
300
|
+
`checker.php` (openorders `nsSub`) and `fixer.php` (`findOpenOrderDiscrepancies`). After the fix all three
|
|
301
|
+
categories report ✓ in sync — there was never real drift, so **no production write was needed**.
|
|
302
|
+
**Diagnostic that nailed it:** a per-line probe dumping NS `transactionline`
|
|
303
|
+
(`lineid`/`item`/`itemtype`/`qty`/`billed`/`rate`/`costestimate` + recomputed profit) alongside the stored
|
|
304
|
+
`OpenOrderItems` rows, which revealed the NULL `rate` on the cost-only lines. (This is a *distinct*
|
|
305
|
+
convention-mismatch from the round-then-sum gotcha above — there the divergence was rounding; here it is
|
|
306
|
+
NULL handling. Both are "FIND and FIX must compute identically.")
|
|
307
|
+
- **INVARIANT: checker FIND, fixer FIND, and fixer FIX must compute a given dimension IDENTICALLY, or `--fix`
|
|
308
|
+
chases phantoms forever.** Any divergence in inclusion filter, rounding, or NULL handling between the three
|
|
309
|
+
passes manifests as a discrepancy FIND flags and FIX refuses to resolve (`ins=0/upd=0/del=0`), looping
|
|
310
|
+
forever. Both the round-then-sum and the NULL-rate gotchas above are instances. When adding/changing a
|
|
311
|
+
compared dimension, change all three sites in lockstep. `listOpportunities()`
|
|
312
|
+
(`library/app/api/netsuite/rest.php`) returns line items with `amount = -foreignamount` and
|
|
313
|
+
`costEstimate = -costestimate`, so the REST-itemList sum and the SuiteQL line-sum are **algebraically
|
|
314
|
+
identical** — that equivalence is what lets fixer's FIX (REST itemList) and FIND (SuiteQL) agree on
|
|
315
|
+
opportunity profit.
|
|
316
|
+
- **PHASE 3 post-fix re-check ignores `--category` and re-runs ALL three FIND passes (known inefficiency,
|
|
317
|
+
NOT yet fixed).** `fixer.php` PHASE 3 (post-fix re-check) re-runs all three category FIND passes
|
|
318
|
+
unconditionally, ignoring `--category` / `$runCats`. The **Sales** per-transaction aggregate SuiteQL
|
|
319
|
+
(~694K-row Sales table, paginated 1000/page) is the heaviest op in the tool, so even a category-scoped run
|
|
320
|
+
(e.g. openorders-only) triggers a **minute-plus full Sales re-scan that looks like a hang**. Suggested fix
|
|
321
|
+
(not applied): gate each PHASE 3 FIND on `in_array($cat, $runCats)`.
|
|
272
322
|
- **Compare money at 2 decimals.** DB columns store 2dp but PHP `revenue - cost` carries float
|
|
273
323
|
dust (`313.6` vs `313.60000001`); raw `!=` produced thousands of phantom UPDATEs that re-wrote
|
|
274
324
|
identical values (1,839 on one open-orders run). Both tools now compare `round((float)$x, 2)`.
|
|
@@ -360,6 +410,23 @@ None — Forecast2 is a single shared dataset.
|
|
|
360
410
|
|
|
361
411
|
## Change history
|
|
362
412
|
|
|
413
|
+
- 2026-06-29 — **Found/fixed the NULL-rate phantom open-order profit drift; extended PROFIT comparison to all
|
|
414
|
+
three categories.** Cost-only NS open-order lines (zero revenue) can have a **NULL `rate`**; the FIND
|
|
415
|
+
profit SQL did `tl.rate - unitCost` and `NULL - x = NULL` silently dropped the line from `SUM`, inflating
|
|
416
|
+
NS profit, while FIX/importer treat NULL rate as 0 — a permanent phantom (Δprofit $0.54 on identical $12.20
|
|
417
|
+
SOs; $1,365–$9,500 on $0-revenue SOs) FIX reported `ins/upd/del=0` on forever. Revenue never showed it ($0
|
|
418
|
+
either way). **Fix:** `NVL(tl.rate,0)` in the open-order revenue AND profit expressions in **both**
|
|
419
|
+
`checker.php` (openorders `nsSub`) and `fixer.php` (`findOpenOrderDiscrepancies`); all three categories then
|
|
420
|
+
✓ in sync, no prod write needed. Localized via a per-line NS `transactionline` probe. Also: checker/fixer
|
|
421
|
+
now compare **profit for Open Orders and Opportunities**, not just Sales — open-order profit
|
|
422
|
+
`qtyOpen*(rate-unitCost)` round-then-sum per line; opportunity profit `Σ(amount−costEstimate)` excluding
|
|
423
|
+
`itemtype='Group'` (= `ITEM_GROUP`, new `OPP_EXCLUDED_ITEMTYPES` constant), revenue = header
|
|
424
|
+
`projectedtotal`; `fixer.fixOpportunities()` now writes `projectedProfit` (previously never did) from the
|
|
425
|
+
`listOpportunities()` itemList. Recorded the cross-toolchain **invariant**: checker FIND, fixer FIND, and
|
|
426
|
+
fixer FIX must compute each dimension identically or `--fix` chases phantoms. **Known inefficiency (not
|
|
427
|
+
fixed):** `fixer.php` PHASE 3 post-fix re-check re-runs all three FIND passes ignoring `--category`, so a
|
|
428
|
+
scoped run still triggers the heavy ~694K-row Sales re-scan (looks like a hang); suggested gate on
|
|
429
|
+
`in_array($cat,$runCats)`. (dfranks)
|
|
363
430
|
- 2026-06-26 — **Added `checker.php` (moment-fingerprint drift detector) + `looper.php` (loop runner), and
|
|
364
431
|
found/fixed the open-orders round-then-sum rounding gotcha.** `checker.php` confirms sync in seconds and
|
|
365
432
|
localizes real drift via a 5-number per-bucket fingerprint (`n,rev,profit,idSum,idSq`; power-sums mod the
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [Worker (worker2) Architecture](architecture.md) | Worker (repo `worker2`) is an AWS Elastic Beanstalk **Worker Tier** application that processes background jobs. | worker2/Controller/Index.php, worker2/Worker/, worker2/LambdaFunctions/, _underscore/Worker.php |
|
|
6
6
|
| [ClickUp Connectivity Watchdog](features/clickup-connectivity-watchdog.md) | A cron watchdog that emails when the ClickUp integration looks disconnected during business hours. | worker2/Worker/Clickup/Health.php, worker2/Database/ClickupHealthWatchdog.sql |
|
|
7
|
+
| [ClickUp GitHub-tab Auto-linking & Ticket-id Branch Naming](features/clickup-github-autolink.md) | How ClickUp surfaces branches/PRs/commits in a ticket's **GitHub tab**, and the branch / PR-title naming convention that triggers it. | |
|
|
7
8
|
| [ClickUp Project & Opportunity Multi-List Routing](features/clickup-project-routing.md) | Routes ClickUp tasks into the correct **secondary multi-list memberships** based on their custom-field values, via the `clickup` webhook. | worker2/Worker/Clickup/Project.php, worker2/Worker/Clickup.php |
|
|
8
9
|
| [ClickUp Rich-Text Custom Fields via Quill Delta (API)](features/clickup-richtext-api.md) | ClickUp custom text fields (type `text` and long-text) support rich formatting only through a **Quill Delta** written to the undocumented `value_richtext` key o | test/@dave/clickup_md2delta.js, .claude/skills/plan-ticket/scripts/clickup.js |
|
|
9
10
|
| [ClickUp Work Type Automation (Committed / Conditional / Stretch)](features/clickup-work-type-automation.md) | The ClickUp webhook handler (`_Worker_Clickup`) automatically maintains each task's **Work Type** custom field — `Committed`, `Conditional`, or `Stretch` — base | worker2/Worker/Clickup.php, worker2/Tests/Worker/ClickupWorkTypeTest.php |
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: ClickUp GitHub-tab Auto-linking & Ticket-id Branch Naming
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: worker2
|
|
5
|
+
project: Worker
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-06-29
|
|
10
|
+
owners: [dfranks]
|
|
11
|
+
files: []
|
|
12
|
+
related:
|
|
13
|
+
- ../workflows/ticket-to-pseudocode-planning.md
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Summary
|
|
17
|
+
|
|
18
|
+
How ClickUp surfaces branches/PRs/commits in a ticket's **GitHub tab**, and the branch /
|
|
19
|
+
PR-title naming convention that triggers it. This is the linkage the `/work-ticket` skill
|
|
20
|
+
depends on when it ships PRs for a planned ticket.
|
|
21
|
+
|
|
22
|
+
## How it works
|
|
23
|
+
|
|
24
|
+
- ClickUp's **native GitHub integration** automatically surfaces a branch, PR, or commit in a
|
|
25
|
+
ticket's GitHub tab **whenever the ticket id appears in the branch name and/or the PR
|
|
26
|
+
title/body**. The match is on the textual ticket id (e.g. `TRUE-79868`).
|
|
27
|
+
- **There is no ClickUp API call to create this link.** The `plan-ticket/scripts/clickup.js`
|
|
28
|
+
helper exposes only `get` and `push` (the latter writes the `📝 Pseudocode` field). Linking
|
|
29
|
+
is purely a side effect of the ticket id appearing in Git metadata.
|
|
30
|
+
- Therefore the durable rule for ticket work: name the **git branch as the BARE ticket id**
|
|
31
|
+
(e.g. `TRUE-79868`), **one branch per repo**, and include the ticket id in the PR body.
|
|
32
|
+
PRs default to base branch **`_main`**.
|
|
33
|
+
|
|
34
|
+
## Branch naming overrides the generic git-workflow rule
|
|
35
|
+
|
|
36
|
+
⚠ This intentionally **overrides** the generic `feature/<short-description>` type-prefix rule
|
|
37
|
+
in `.claude/rules/toga/common/git-workflow.md` **for ticket-driven work**. The bare ticket-id
|
|
38
|
+
branch name is exactly what ClickUp's native GitHub integration auto-links on, so a
|
|
39
|
+
`feature/…` prefix would break the auto-link. Confirmed against live repos already on
|
|
40
|
+
`TRUE-79142` (worker) and `TRUE-77219` (api2).
|
|
41
|
+
|
|
42
|
+
## Change history
|
|
43
|
+
|
|
44
|
+
- 2026-06-29 — Initial doc: ClickUp native GitHub integration auto-links any branch/PR carrying the ticket id (no API call; clickup.js exposes only get/push); convention is bare-ticket-id branch name (one per repo) + ticket id in PR body, base `_main`. Bare-id naming intentionally overrides git-workflow.md's feature/ prefix for ticket work, confirmed from live TRUE-79142 (worker) and TRUE-77219 (api2) branches (dfranks)
|
|
@@ -6,13 +6,14 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-29
|
|
10
10
|
owners: [dfranks]
|
|
11
11
|
files:
|
|
12
12
|
- test/@dave/clickup_md2delta.js
|
|
13
13
|
related:
|
|
14
14
|
- ../features/clickup-richtext-api.md
|
|
15
15
|
- ../features/talos-meeting-notes-integration.md
|
|
16
|
+
- ../features/clickup-github-autolink.md
|
|
16
17
|
---
|
|
17
18
|
|
|
18
19
|
## Summary
|
|
@@ -55,6 +56,34 @@ under `test/@dave/approach/`):
|
|
|
55
56
|
**Architecture decision** · **Phases** (each with file paths + pseudocode) · **Testing** ·
|
|
56
57
|
**Risks** · **Owners / open questions** · **Key files**.
|
|
57
58
|
|
|
59
|
+
## Downstream: plan → work pipeline
|
|
60
|
+
|
|
61
|
+
Planning is the **first half** of a two-skill pipeline. Once the plan is approved and pushed
|
|
62
|
+
to the ticket's `📝 Pseudocode` field, the **downstream `/work-ticket <TICKET-ID>` skill**
|
|
63
|
+
(also a local skill, not in the team repo) implements it:
|
|
64
|
+
|
|
65
|
+
1. **Loads the approved plan FROM the ticket's `📝 Pseudocode` field** — the field is the
|
|
66
|
+
source of truth, not the local `approach/<TICKET>.md`. It reads via
|
|
67
|
+
`plan-ticket/scripts/clickup.js get <TICKET>`, which returns JSON
|
|
68
|
+
`{id, custom_id, name, status, description, pseudocodeFieldId, url, fields:{…}}` where
|
|
69
|
+
`fields` is the non-empty custom fields keyed by display name. The plan is the `fields`
|
|
70
|
+
entry whose key contains "Pseudocode" (case-insensitive), as plain text. **Stops if empty.**
|
|
71
|
+
2. **Primes framework context** (self-answered `/kickoff`) from the plan's
|
|
72
|
+
**Repos / Framework / Client** header line.
|
|
73
|
+
3. **Implements phase-by-phase** with implementer subagents (worktree-isolated for independent
|
|
74
|
+
repos) + TOGA reviewers (php-reviewer / sql-reviewer) looping until clean.
|
|
75
|
+
4. **Per repo in the plan's Repos header:** branch → commit → push, then opens **one PR per
|
|
76
|
+
repo** with `gh` (default base branch `_main`).
|
|
77
|
+
5. **Reports and STOPS.**
|
|
78
|
+
|
|
79
|
+
**Autonomy boundary (decided):** `/work-ticket` runs autonomously through PR creation, then
|
|
80
|
+
stops. It **never changes the ClickUp ticket status** — advancing the ticket for approval is
|
|
81
|
+
always a manual developer action, because the developer must review the PR(s) first.
|
|
82
|
+
|
|
83
|
+
See [ClickUp GitHub-tab auto-linking & ticket-id branch naming](../features/clickup-github-autolink.md)
|
|
84
|
+
for the branch/PR naming convention `/work-ticket` relies on.
|
|
85
|
+
|
|
58
86
|
## Change history
|
|
59
87
|
|
|
88
|
+
- 2026-06-29 — Added the downstream half: `/plan-ticket` → `/work-ticket` pipeline. Records how `/work-ticket` loads the approved plan from the `📝 Pseudocode` field (clickup.js `get` JSON shape, "Pseudocode"-keyed plain-text value), implements phase-by-phase with reviewer loops, branches/commits/PRs per repo, and STOPS at PR creation — never touching ClickUp status (manual gate) (dfranks)
|
|
60
89
|
- 2026-06-26 — Initial doc: ticket→pseudocode planning procedure (ClickUp read → codebase investigation → Talos DevCore grounding → phased plan → approval gate → guarded richtext push); records the shape, not the local skill source (dfranks)
|
package/knowledge/INDEX.md
CHANGED
|
@@ -17,7 +17,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
17
17
|
## 2.0 framework
|
|
18
18
|
|
|
19
19
|
- **_underscore** (_Underscore) _(framework core)_ — 17 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
20
|
-
- **worker2** (Worker) —
|
|
20
|
+
- **worker2** (Worker) — 21 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
21
21
|
- **api2** (API) — 7 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
22
22
|
- **dbchanges2** (Database Changes) _(framework core)_ — 3 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
|
|
23
23
|
- **toga2-supply** (TOGa Supply) — 3 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
|
package/package.json
CHANGED