toga-ai 1.0.292 → 1.0.294
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,7 +10,7 @@
|
|
|
10
10
|
| [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 |
|
|
11
11
|
| [_Component_*/_Model_* project-namespace registration (autoloader) & backslash-qualify traps](features/component-model-namespace-registration.md) | Every **project-local** `_Component_*` and `_Model_*` class in a 2.0 app **must declare the project namespace** at the top of the file: ```php namespace <NAMESP | _underscore/Loader.php, worker2/_.php, api2/_.php, worker2/Component/Forecast/Db/Db.php, worker2/Component/Forecast/SaleImport/SaleImport.php, api2/Component/Api/Netsuite/Netsuite.php |
|
|
12
12
|
| [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 |
|
|
13
|
-
| [Error Reporting — Issue/Event Aggregation (
|
|
13
|
+
| [Error Reporting — Issue/Event Aggregation (agreed POST-to-receiver design)](features/error-reporting-issue-event.md) | Platform-wide error-reporting infrastructure for TOGA 2.0, built around a two-table **Issue / Event** aggregation model in the shared **Core Logs DB**. | _underscore/Error.php, _underscore/Model/Core/Logs/Issue.php, _underscore/Model/Core/Logs/Event.php, dbchanges2/Logs/2026-07-06 - Issue and Event tables.sql |
|
|
14
14
|
| [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 |
|
|
15
15
|
| [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/fixer.php, test/@dave/Junk Drawer/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/Junk Drawer/NetSuite/api-message-queue/dev_ue_api_msg_queue_enqueue.js |
|
|
16
16
|
| [Item-Fulfillment Stage Lifecycle (picked/packed/shipped) & Order Status](features/item-fulfillment-stage-lifecycle-and-order-status.md) | Every ItemFulfillment (IF) now carries an explicit **stage** — picked → packed → shipped — resolved through `ItemFulfillmentStages → ItemFulfillmentStatuses` (m | _underscore/Model/Client/SalesOrder.php, _underscore/Model/Quad/SalesOrder.php, _underscore/Model/Compass/SalesOrder.php, _underscore/Model/Compass/SalesOrderStatus.php, _underscore/Model/Client/SalesOrderItem.php, _underscore/Model/Client/Item.php, _underscore/Model/Client/PurchaseOrderItem.php, library/app/api/toga2.php, dbchanges2/Client/2026-06-30a - BackfillNullStageItemFulfillmentsToShipped.sql, dbchanges2/Client/2026-06-30b - SalesOrderStatusesPickedPacked.sql, dbchanges2/Client/2026-06-30c - ItemFulfillmentStageIdNotNull.sql, dbchanges2/Client_CompassCanada/2026-06-30a - ItemFulfillmentLifecycleAndShippedBackfill.sql |
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Error Reporting — Issue/Event Aggregation (
|
|
2
|
+
title: Error Reporting — Issue/Event Aggregation (agreed POST-to-receiver design)
|
|
3
3
|
framework: "2.0"
|
|
4
4
|
repo: _underscore
|
|
5
5
|
project: _Underscore
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-08
|
|
10
10
|
owners: ["dfranks"]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Error.php
|
|
@@ -21,39 +21,51 @@ related:
|
|
|
21
21
|
|
|
22
22
|
## Summary
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
Platform-wide error-reporting infrastructure for TOGA 2.0, built around a two-table
|
|
25
|
+
**Issue / Event** aggregation model in the shared **Core Logs DB**. An **Issue** is the
|
|
26
|
+
deduplicated record for a distinct error (keyed by a hash of the stack trace) carrying an
|
|
27
|
+
aggregate `occurrences` counter and an escalatable `urgency`; an **Event** is one row per
|
|
28
|
+
individual occurrence with its own trace and context.
|
|
29
|
+
|
|
30
|
+
**The agreed architecture (see below) decouples *reporting* from *persistence*:**
|
|
31
|
+
application error handlers **POST** error payloads to a centralized `/errors` receiver
|
|
32
|
+
endpoint; a **worker2 cron** consumes them and does all the hashing, upserting, aggregation,
|
|
33
|
+
escalation, and ClickUp sync into the Core Logs DB. Handlers do **not** write Issue/Event
|
|
34
|
+
rows to the database directly. This is the canonical target design for the whole
|
|
35
|
+
error-monitoring epic (TRUE-781xx) — the handler ticket, the dashboard ticket, and the
|
|
36
|
+
escalation-cron ticket must all conform to it.
|
|
37
|
+
|
|
38
|
+
## Agreed error-monitoring architecture
|
|
39
|
+
|
|
40
|
+
Source: the **"2026-05-21 - Errors, Monitoring and Alerts"** meeting design (approved;
|
|
41
|
+
cited via Talos DevCore meeting notes). Pipeline:
|
|
42
|
+
|
|
43
|
+
1. **Report (application side).** An application's error/exception handler serializes the
|
|
44
|
+
error (message, stack trace, context) and **POSTs it to a centralized `/errors` receiver
|
|
45
|
+
endpoint** (the meeting referenced `webhook.hub.com/error`). The handler's only job is to
|
|
46
|
+
transmit — it performs **no direct database persistence**.
|
|
47
|
+
2. **Ingest (worker2 cron).** A worker2 cron consumes the received error payloads and:
|
|
48
|
+
- builds `issueHash` from the stack trace to identify the distinct error,
|
|
49
|
+
- **upserts an Issue** and **inserts an Event** into the **Core** Logs DB,
|
|
50
|
+
- aggregates occurrence counts within a time window,
|
|
51
|
+
- auto-escalates / de-escalates the Issue `urgency` based on occurrence frequency,
|
|
52
|
+
- syncs the Issue to ClickUp.
|
|
53
|
+
3. **Scope.** API 2.0 handlers first; 1.0 is deferred.
|
|
54
|
+
|
|
55
|
+
The critical rule: **handlers POST; the cron persists (to Core).** Any approach where the
|
|
56
|
+
handler writes Issue/Event rows inline to a database contradicts this design.
|
|
57
|
+
|
|
58
|
+
## Data model (Core Logs DB)
|
|
59
|
+
|
|
39
60
|
- **`_underscore/Model/Core/Logs/Issue.php`** — `const DATABASE = _underscore::DB_LOGS;
|
|
40
61
|
const TABLE = 'Issues';`. Fields: `id, uuid, dtCreated, dtLastOccurred, urgency, subject,
|
|
41
|
-
description, clickupIdentifier, hash, errorMessage, occurrences, trace`.
|
|
62
|
+
description, clickupIdentifier, hash, errorMessage, occurrences, trace`. One row per
|
|
63
|
+
distinct error (dedup key = `hash`); `occurrences` is the rolling count, `dtLastOccurred`
|
|
64
|
+
the most-recent hit, `urgency` a list field, `clickupIdentifier` links to an escalation
|
|
65
|
+
ticket. There is **no `type` column**.
|
|
42
66
|
- **`_underscore/Model/Core/Logs/Event.php`** — `const DATABASE = _underscore::DB_LOGS;
|
|
43
67
|
const TABLE = 'Events';`. Fields: `id, uuid, issueId (FK → Issue), errorMessage, trace,
|
|
44
|
-
context, dtCreated`.
|
|
45
|
-
|
|
46
|
-
## How it works
|
|
47
|
-
|
|
48
|
-
1. On an uncaught exception, hash the backtrace to identify the distinct error.
|
|
49
|
-
2. Load the existing `Issue` by `hash`. On a miss, create it with `errorMessage`, `trace`,
|
|
50
|
-
`urgency = 'LOW'`, and `occurrences = 0`.
|
|
51
|
-
3. **Increment `occurrences`** (`($issue->occurrences ?? 0) + 1`) and set
|
|
52
|
-
`dtLastOccurred`, then save — so the counter advances on every occurrence, hit or new.
|
|
53
|
-
4. Insert one `Event` for this occurrence: `issueId`, `errorMessage`, per-occurrence
|
|
54
|
-
`trace`, and `context` (`print_r($GLOBALS, true)`).
|
|
55
|
-
5. Commit the `DB_LOGS` transaction; surface `Error {issueId}, Event {eventId}` in the
|
|
56
|
-
debug body.
|
|
68
|
+
context, dtCreated`. One row per occurrence.
|
|
57
69
|
|
|
58
70
|
The `Issues`/`Events` tables live in the shared **Core Logs** DB, provisioned from
|
|
59
71
|
`dbchanges2/Logs/` (the `Logs/` folder targets the framework-level, non-tenant `Logs` DB
|
|
@@ -61,35 +73,55 @@ with unqualified table names — see the dbchanges2 architecture folder→DB map
|
|
|
61
73
|
with per-client API/error logging in `Logs_<Tenant>` via `Model/Client/Logs/*`
|
|
62
74
|
(`DB_CLIENT_LOGS`, `dbchanges2/Logs_Client/`).
|
|
63
75
|
|
|
64
|
-
##
|
|
76
|
+
## Current state of `_underscore/Error.php` (important — reads differently than you'd expect)
|
|
77
|
+
|
|
78
|
+
`_underscore/Error.php` registers `exceptionHandler()` via `set_exception_handler`, but in
|
|
79
|
+
**`_production` it does NOT persist any Issue/Event rows.** It builds a debug HTML body,
|
|
80
|
+
writes it to an S3 error-details file, and (when a Logs register is present) records a
|
|
81
|
+
single `_Model_Client_Logs_Error` / `_Model_Core_Logs_Error` row — the older flat error log.
|
|
82
|
+
There is no Issue/Event aggregation, no `issueHash` upsert, and no ClickUp escalation in the
|
|
83
|
+
handler on `_production`.
|
|
65
84
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
85
|
+
**An inline-DB-persist approach exists on an in-flight branch, but it conflicts with the
|
|
86
|
+
agreed design and must not be treated as the model to follow:** that approach has the
|
|
87
|
+
handler compute `$issueHash = md5(serialize($backtrace))` and directly upsert
|
|
88
|
+
`_Model_Client_Logs_Issue` + insert `_Model_Client_Logs_Event`, committing a transaction —
|
|
89
|
+
i.e. handler-side direct persistence, and to the **Client** Logs DB rather than **Core**.
|
|
90
|
+
That contradicts the approved architecture on two counts: (a) handlers should POST to the
|
|
91
|
+
`/errors` receiver, not persist; and (b) aggregation belongs in the Core Logs DB, driven by
|
|
92
|
+
the worker2 cron. New work (TRUE-78178 handlers) should implement the POST-to-receiver path,
|
|
93
|
+
not extend the inline-persist branch.
|
|
71
94
|
|
|
72
95
|
## Gotchas / known issues
|
|
73
96
|
|
|
74
|
-
-
|
|
75
|
-
|
|
97
|
+
- **Do not assume error persistence "already works."** On `_production` the handler writes
|
|
98
|
+
only an S3 debug body and a flat `*_Logs_Error` row — no Issue/Event aggregation exists in
|
|
99
|
+
production yet. The Issue/Event model is the *target*, delivered by the POST→cron pipeline.
|
|
100
|
+
- **`occurrences` must be incremented, not just seeded** — a naive upsert that sets
|
|
101
|
+
`occurrences = 0` only at creation never advances the aggregate counter. The cron must
|
|
76
102
|
increment on every occurrence.
|
|
77
|
-
- **`Event.trace` must be
|
|
78
|
-
|
|
79
|
-
- **Persistence
|
|
80
|
-
`\_Database::$_registers[_underscore::DB_LOGS]` isn't set,
|
|
81
|
-
skipped
|
|
103
|
+
- **`Event.trace` must be populated per occurrence** — the per-Event `trace` column is easy
|
|
104
|
+
to leave empty; record it on every Event.
|
|
105
|
+
- **Persistence must be conditional on the Core-Logs register.** If the Core Logs register
|
|
106
|
+
(`\_Database::$_registers[_underscore::DB_LOGS]`) isn't set, an Issue/Event write must be
|
|
107
|
+
skipped rather than fatal — but this belongs in the cron, not the handler.
|
|
108
|
+
- **Client vs Core Logs DB mix-up.** The Issue/Event tables belong in the **Core** Logs DB
|
|
109
|
+
(`DB_LOGS`, `Model/Core/Logs/*`). Writing them to a per-client Logs DB
|
|
110
|
+
(`DB_CLIENT_LOGS`, `_Model_Client_Logs_*`) is the wrong target and does not match the
|
|
111
|
+
agreed design.
|
|
82
112
|
- **Shared-schema migration collision.** Because `Issues`/`Events` live in the *shared* Core
|
|
83
|
-
Logs DB,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
`IssueEmailAddresses` table, one adding this error-class persistence). Only **one**
|
|
87
|
-
migration may create the shared tables; reconcile overlapping `Logs/` create-scripts at
|
|
88
|
-
merge so the tables are created exactly once and later migrations only `ALTER`.
|
|
113
|
+
Logs DB, two dbchanges2 `Logs/` migrations that both `CREATE TABLE Issues`/`Events` will
|
|
114
|
+
collide ("table already exists"). Only **one** migration may create the shared tables;
|
|
115
|
+
later migrations only `ALTER`.
|
|
89
116
|
|
|
90
117
|
## Change history
|
|
91
118
|
|
|
92
|
-
- 2026-07-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
119
|
+
- 2026-07-08 — Corrected KB drift: documented the approved 2026-05-21 error-monitoring
|
|
120
|
+
architecture (handlers POST to a centralized `/errors` receiver; a worker2 cron builds
|
|
121
|
+
issueHash, upserts Issues + inserts Events into the Core Logs DB, aggregates/escalates,
|
|
122
|
+
syncs ClickUp — handlers do not persist). Clarified that `_production` Error.php performs
|
|
123
|
+
no Issue/Event persistence (only an S3 debug body + flat `*_Logs_Error` row), and that the
|
|
124
|
+
inline-DB-persist approach (handler-side upsert to the *Client* Logs DB) conflicts with the
|
|
125
|
+
agreed design and is not the pattern to follow. (dfranks)
|
|
126
|
+
- 2026-07-06 — Prior doc described the exceptionHandler as directly persisting Issue/Event
|
|
127
|
+
rows to the Core Logs DB with dedup; superseded by the 2026-07-08 correction above. (dfranks)
|
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: work-ticket
|
|
3
|
-
description: Work an APPROVED ClickUp ticket plan end-to-end. Invoke as `/work-ticket <TICKET-ID>` (e.g. `/work-ticket TRUE-79868`) AFTER `/plan-ticket` has pushed an approved plan to the ticket's Pseudocode field. Loads the plan from the ticket's Pseudocode field, primes framework context via /kickoff (self-answered), implements the plan phase-by-phase with TOGA reviewers, then
|
|
3
|
+
description: Work an APPROVED ClickUp ticket plan end-to-end. Invoke as `/work-ticket <TICKET-ID>` (e.g. `/work-ticket TRUE-79868`) AFTER `/plan-ticket` has pushed an approved plan to the ticket's Pseudocode field. Loads the plan from the ticket's Pseudocode field, primes framework context via /kickoff (self-answered), per repo creates and checks out a bare ticket-ID branch from the fresh remote default BEFORE any edits, implements the plan phase-by-phase with TOGA reviewers, then commits, pushes, opens a PR, and posts the PR links as a comment on the ticket. Stops after PRs are open for your review — never changes ClickUp ticket status. Trigger on "/work-ticket", "work the ticket", "execute the plan for <ticket>", "build and PR <ticket>", "ship <ticket>".
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# work-ticket — approved plan →
|
|
6
|
+
# work-ticket — approved plan → branch → code → commit → PR → ClickUp GitHub tab
|
|
7
7
|
|
|
8
8
|
The downstream companion to **`/plan-ticket`**. Given a ticket whose plan has already been
|
|
9
9
|
approved and pushed to its **📝 Pseudocode** field, this skill executes that plan: it primes
|
|
10
|
-
context,
|
|
11
|
-
**bare ticket id
|
|
10
|
+
context, then for **each repo the plan touches** creates and checks out a branch named the
|
|
11
|
+
**bare ticket id** (cut from the freshly-fetched remote default) **BEFORE writing any code**,
|
|
12
|
+
writes the code on that branch, commits, pushes, opens a PR, and posts the PR links as a
|
|
13
|
+
ticket comment.
|
|
12
14
|
ClickUp's native GitHub integration *may* surface those PRs in the ticket's **GitHub tab** when
|
|
13
|
-
the repo is connected in ClickUp's GitHub settings (see Step
|
|
15
|
+
the repo is connected in ClickUp's GitHub settings (see Step 6b — not guaranteed), because the
|
|
14
16
|
ticket id is in the
|
|
15
17
|
branch name (and PR title/body).
|
|
16
18
|
|
|
@@ -62,14 +64,52 @@ Pass them as the trailing argument so kickoff goes straight to preflight + primi
|
|
|
62
64
|
/kickoff <framework> <layer>, repos: <repos>, client: <client> — execute <TICKET> <title>
|
|
63
65
|
```
|
|
64
66
|
|
|
65
|
-
Carry the `context-primer` briefing (framework rules, gotchas, client variations) into Step
|
|
67
|
+
Carry the `context-primer` briefing (framework rules, gotchas, client variations) into Step 4 —
|
|
66
68
|
it directly informs how each phase is implemented. Only stop to ask the developer if a kickoff
|
|
67
69
|
gate answer is genuinely ambiguous after reading the plan header.
|
|
68
70
|
|
|
69
|
-
## Step 3 —
|
|
71
|
+
## Step 3 — Per repo: create & checkout the ticket branch BEFORE any edits
|
|
72
|
+
|
|
73
|
+
**Branch FIRST, edit SECOND — never write a single line while sitting on `_main`/`_production`
|
|
74
|
+
or on another ticket's branch.** Two reasons this ordering is mandatory:
|
|
75
|
+
|
|
76
|
+
1. **Safety** — if work-in-progress gets committed (by you, a hook, or the developer), it lands
|
|
77
|
+
on the ticket branch, never on a default branch that deploys to prod.
|
|
78
|
+
2. **Fresh base** — edits are made against the CURRENT remote default, not a stale local
|
|
79
|
+
checkout. (Real incident: a local `_production` was 12 commits behind and the target file had
|
|
80
|
+
changed upstream — editing before branching would have based the PR on stale code and
|
|
81
|
+
silently reverted the upstream changes.)
|
|
82
|
+
|
|
83
|
+
For **each repo in the plan's `Repos:` header**, run inside that repo's path:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# 0. Working tree must be clean before starting — surface any stray edits to the developer
|
|
87
|
+
git -C "<repo-path>" status --porcelain
|
|
88
|
+
# 1. Resolve the repo's REAL default branch (e.g. _production for app repos, _main for dbchanges2)
|
|
89
|
+
DEFAULT=$(gh repo view <owner/repo> --json defaultBranchRef -q .defaultBranchRef.name)
|
|
90
|
+
# 2. Fetch so the base is current
|
|
91
|
+
git -C "<repo-path>" fetch origin --quiet
|
|
92
|
+
# 3. Reuse an existing TRUE-XXXX branch if present; otherwise cut a fresh one from origin/<default>
|
|
93
|
+
git -C "<repo-path>" rev-parse --verify TRUE-XXXX 2>/dev/null \
|
|
94
|
+
&& git -C "<repo-path>" checkout TRUE-XXXX \
|
|
95
|
+
|| git -C "<repo-path>" checkout -b TRUE-XXXX "origin/$DEFAULT"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- Branch name = **bare ticket id** (team convention; what ClickUp's GitHub integration links on).
|
|
99
|
+
- If `git status` shows uncommitted changes from a prior task, **stop and ask the developer**
|
|
100
|
+
before touching that repo — do not stash someone's work silently.
|
|
101
|
+
- If reusing an existing `TRUE-XXXX` branch, still fetch and consider rebasing onto
|
|
102
|
+
`origin/<default>` so the PR diff stays current.
|
|
103
|
+
- **Never branch off / commit to `_main`/`_production` directly**, and **never force-push**
|
|
104
|
+
(per `git-workflow.md`).
|
|
105
|
+
|
|
106
|
+
Only after every in-scope repo is sitting on its ticket branch do you start writing code.
|
|
107
|
+
|
|
108
|
+
## Step 4 — Execute the plan, phase by phase
|
|
70
109
|
|
|
71
110
|
Implement the plan against the **real repo paths** (resolve each from the `repo-path-<repo>`
|
|
72
|
-
memory kickoff established; never guess paths)
|
|
111
|
+
memory kickoff established; never guess paths) — each repo already checked out on its ticket
|
|
112
|
+
branch from Step 3. For non-trivial work drive it as implement →
|
|
73
113
|
verify with subagents so the conversation stays the conductor:
|
|
74
114
|
|
|
75
115
|
1. For each **phase** in the plan, an implementer subagent writes that phase's code at the
|
|
@@ -94,34 +134,13 @@ verify with subagents so the conversation stays the conductor:
|
|
|
94
134
|
|
|
95
135
|
Do **not** push anything to a remote or open a PR until the code is implemented and clean.
|
|
96
136
|
|
|
97
|
-
## Step
|
|
137
|
+
## Step 5 — Per repo: commit & push
|
|
98
138
|
|
|
99
139
|
For **each repo in the plan's `Repos:` header** that has changes, run inside that repo's path:
|
|
100
140
|
|
|
101
|
-
1. **
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
`origin/<default>` — never from whatever local branch the repo happens to be sitting on.** A
|
|
105
|
-
working checkout is frequently parked on a *different, unmerged ticket's branch* (or a stale
|
|
106
|
-
local default); branching off that silently bases your PR on someone else's unmerged work and
|
|
107
|
-
produces a wrong, conflict-prone diff. So always **fetch first, then branch off the remote
|
|
108
|
-
default**, carrying your uncommitted Step 3 changes onto the new branch:
|
|
109
|
-
```bash
|
|
110
|
-
# Resolve the repo's REAL default branch (e.g. _production for app repos, _main for dbchanges2)
|
|
111
|
-
DEFAULT=$(gh repo view <owner/repo> --json defaultBranchRef -q .defaultBranchRef.name)
|
|
112
|
-
git -C "<repo-path>" fetch origin --quiet
|
|
113
|
-
# Reuse an existing TRUE-XXXX branch if present; otherwise cut a fresh one from origin/<default>.
|
|
114
|
-
git -C "<repo-path>" rev-parse --verify TRUE-XXXX 2>/dev/null \
|
|
115
|
-
&& git -C "<repo-path>" checkout TRUE-XXXX \
|
|
116
|
-
|| git -C "<repo-path>" checkout -b TRUE-XXXX "origin/$DEFAULT"
|
|
117
|
-
```
|
|
118
|
-
`git checkout -b … origin/$DEFAULT` keeps your uncommitted working-tree edits and re-bases them
|
|
119
|
-
onto the latest remote default in one step. **First confirm `git status` shows only the files
|
|
120
|
-
your plan changed** (no stray edits from a prior ticket) before branching. If the checkout is
|
|
121
|
-
refused because a file you edited also changed on the default, `git stash` → `checkout -b … origin/$DEFAULT`
|
|
122
|
-
→ `git stash pop` and resolve. **Never branch off / commit to `_main`/`_production` directly**,
|
|
123
|
-
and **never force-push** (per `git-workflow.md`). If already on the `TRUE-XXXX` branch, still
|
|
124
|
-
`git fetch` and consider rebasing onto `origin/<default>` so the PR diff stays current.
|
|
141
|
+
1. **Verify the branch.** Confirm `git branch --show-current` is the `TRUE-XXXX` branch from
|
|
142
|
+
Step 3 — if the repo somehow ended up back on a default branch, go redo Step 3 for it before
|
|
143
|
+
committing anything.
|
|
125
144
|
2. **Commit.** Stage only the files the plan changed. Message format `type: short description`
|
|
126
145
|
(`feat`/`fix`/`refactor`/`docs`/`test`/`chore`, lowercase, present tense, ≤72 chars). Add a
|
|
127
146
|
body paragraph for substantial changes. **Pre-commit check:** `php -l` passes on changed PHP,
|
|
@@ -130,7 +149,7 @@ For **each repo in the plan's `Repos:` header** that has changes, run inside tha
|
|
|
130
149
|
|
|
131
150
|
Repeat for every in-scope repo — one branch per repo, all named the same bare ticket id.
|
|
132
151
|
|
|
133
|
-
## Step
|
|
152
|
+
## Step 6 — Open a PR per repo (auto-links to the ClickUp GitHub tab)
|
|
134
153
|
|
|
135
154
|
For each pushed repo, open a PR with `gh` from that repo's path:
|
|
136
155
|
|
|
@@ -158,7 +177,7 @@ gh pr create --repo <owner/repo> --base <DEFAULT-BRANCH> --head <TICKET> \
|
|
|
158
177
|
- End the PR body with the standard footer:
|
|
159
178
|
`🤖 Generated with [Claude Code](https://claude.com/claude-code)`.
|
|
160
179
|
|
|
161
|
-
### Step
|
|
180
|
+
### Step 6b — Post the PR links as a ClickUp comment (the RELIABLE link — do NOT skip)
|
|
162
181
|
|
|
163
182
|
**The native GitHub↔ClickUp tab is NOT reliable in this workspace.** It only populates if each
|
|
164
183
|
repo is connected under ClickUp → Settings → Integrations → GitHub (a one-time workspace OAuth
|
|
@@ -181,12 +200,12 @@ fetch("https://api.clickup.com/api/v2/task/<TICKET>/comment?custom_task_ids=true
|
|
|
181
200
|
(A comment does NOT populate the GitHub *tab* — it lands in the ticket's activity/comments —
|
|
182
201
|
but it is the dependable way for the developer to reach the PRs from the ticket.)
|
|
183
202
|
|
|
184
|
-
## Step
|
|
203
|
+
## Step 7 — Report & stop (do NOT touch ClickUp status)
|
|
185
204
|
|
|
186
205
|
Summarize for the developer:
|
|
187
206
|
- Ticket title and the repos that got changes.
|
|
188
207
|
- For each repo: the branch name (`<TICKET>`) and the **PR url** `gh` returned.
|
|
189
|
-
- Confirm the Step
|
|
208
|
+
- Confirm the Step 6b comment was posted (the reliable link). State plainly that the GitHub
|
|
190
209
|
**tab** populates only if the repos are connected in ClickUp's GitHub integration settings —
|
|
191
210
|
do not claim it will appear automatically.
|
|
192
211
|
- **Remind the developer the next step is theirs:** review/approve the PR(s), then **manually
|
|
@@ -207,7 +226,7 @@ Offer `/capture` if a durable, KB-worthy finding emerged while implementing.
|
|
|
207
226
|
- **Linking is NOT automatic.** The native GitHub tab requires a per-repo connection in ClickUp's
|
|
208
227
|
GitHub integration settings (workspace OAuth, not doable from `gh`). The branch name + bare-id
|
|
209
228
|
PR title + `CU-<internalId>` in the body are necessary but **not sufficient** — if the repo
|
|
210
|
-
isn't connected, the tab stays empty. Step
|
|
229
|
+
isn't connected, the tab stays empty. Step 6b's comment is the reliable link; always post it.
|
|
211
230
|
- **Autonomy boundary:** autonomous through PR creation; stops there. Moving the ClickUp ticket
|
|
212
231
|
is always a manual developer action.
|
|
213
232
|
- Run AFTER `/plan-ticket` has produced and the developer has approved a plan. This skill does
|