toga-ai 1.0.1024 → 1.0.1026
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/CLAUDE.md +1 -1
- package/knowledge/2.0/apps/_underscore/INDEX.md +1 -0
- package/knowledge/2.0/apps/_underscore/features/case-status-and-stage-gate.md +46 -0
- package/knowledge/2.0/apps/_underscore/features/note-record-authorship.md +7 -2
- package/knowledge/2.0/apps/worker2/features/clickup-github-autolink.md +4 -3
- package/knowledge/INDEX.md +1 -1
- package/knowledge/clients/nycdoe/features/asn-sftp-ingestion-2-0.md +5 -2
- package/knowledge/standalone/apps/claude/workflows/clickup-ticket-workflow.md +22 -16
- package/package.json +1 -1
- package/rules/common/communication.md +20 -1
- package/rules/common/git-workflow.md +22 -10
- package/rules/common/security.md +3 -0
- package/scripts/hooks/start-gate.js +28 -23
- package/skills/finish/SKILL.md +28 -27
- package/skills/pseudocode/SKILL.md +11 -10
- package/skills/start/SKILL.md +14 -9
package/CLAUDE.md
CHANGED
|
@@ -20,7 +20,7 @@ as `/skill-name` inside Claude Code.
|
|
|
20
20
|
|-------|---------|-------------|
|
|
21
21
|
| **start** | `/start TRUE-1234` | **Start of every session.** ClickUp ticket id only. Reads the ticket (apps, clients, task type, pseudocode), checks branches, then primes the framework architecture, coding standards, repo knowledge, and client context. Edits are blocked until it runs. |
|
|
22
22
|
| **commit** | `/commit` | Commit only the files this session changed, with the session notes in the message, then pushes the ticket branch. |
|
|
23
|
-
| **pseudocode** | `/pseudocode` | Major tickets only. Writes the agreed approach as plain text to the ticket's Pseudocode field
|
|
23
|
+
| **pseudocode** | `/pseudocode` | Major tickets only. Writes the agreed approach as plain text to the ticket's Pseudocode field (no separate write approval). |
|
|
24
24
|
| **finish** | `/finish` | **End of every session.** Records what was built or changed into the knowledge base, checks the ticket fields, and when the ticket is done pushes, opens the PRs, comments on the ticket, and sets it to development finished. |
|
|
25
25
|
| **toga-loop** | `/toga-loop` | Orchestrate a bigger or iterative change (multi-file feature, refactor, migration, sweep, or "keep going until X") as an explore→implement→verify loop driven by subagents, with progress tracked on a state file so it survives compaction and can resume. Run after `/start`. |
|
|
26
26
|
| **feature** | `/feature` | Build a feature end-to-end as a subagent-driven loop: restate goal + done-condition, get an independent second opinion on the approach before coding, plan, implement (worktree-isolated for parallel work), verify with reviewers, loop until done, hand off to `/finish`. Run after `/start`. |
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
| [Calculated-column filter options (`<field>FilterOptions` model static → table meta)](features/calculated-column-filter-options.md) | Lets a calculated (`FIELD_SQL`) table column supply its own filter dropdown options via a `<field>FilterOptions` model static; open when adding a data-driven co |
|
|
15
15
|
| [FIELD_SQL calculated fields — the underscore-prefix + same-name-method contract](features/calculated-sql-fields.md) | A `FIELD_SQL` (calculated) field on a `_Model` is bound by a **two-part contract that `_Model` enforces by throwing at model-construction time**, not by convent |
|
|
16
16
|
| [Carrier Shipping Labels (UPS/FedEx) & NetSuite Item Fulfillment](features/carrier-shipping-labels.md) | Backend mechanics behind Fulfill & Ship: buying/persisting a carrier label (UPS/FedEx) and creating the NetSuite Item Fulfillment; open for label, reprint, retu |
|
|
17
|
+
| [Case status is derived from stage/owner; stage changes pass a reason-note gate](features/case-status-and-stage-gate.md) | `_Model_Client_Case::_status` is DERIVED (never set) and stage writes via api2 are gated by a `case-set-stage` Surface rule; open before writing Cases from code |
|
|
17
18
|
| [Running 2.0 code from a bare CLI script (bootstrap + transactions)](features/cli-script-bootstrap.md) | Minimum recipe + traps for running real 2.0 framework code (`_Model`/`_Query`/`_Database`) from a bare `php script.php`; open before writing a CLI harness/backf |
|
|
18
19
|
| [Client Users require a contactId — the postPost backfill interceptor & every creation path](features/client-user-contactid-required.md) | Every `Client_<Tenant>.Users` row must have a non-null `contactId`; the shared `_Model_Client_User::postPost` interceptor guarantees it for api2-created users, |
|
|
19
20
|
| [_Cloud credentials (WorkloadsRuntime) + S3 helpers (copy / get / delete / list)](features/cloud-s3-helpers.md) | `_Cloud` credentials (assumes `WorkloadsRuntime`, no IAM keys) and S3 helpers (copy/get/delete/list, per-bucket region, 5 GB single-PUT limit); open before any |
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Case status is derived from stage/owner; stage changes pass a reason-note gate"
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: _underscore
|
|
5
|
+
project: _Underscore
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-10-15
|
|
10
|
+
owners: [jcardinal]
|
|
11
|
+
files:
|
|
12
|
+
- _underscore/Model/Client/Case.php
|
|
13
|
+
- _underscore/Model/Client/CaseNote.php
|
|
14
|
+
- _underscore/Model/Client/CaseStage.php
|
|
15
|
+
- _underscore/Model/Client/Ticket.php
|
|
16
|
+
related:
|
|
17
|
+
- ../../api2/features/api-payload-interceptors.md
|
|
18
|
+
- ./note-record-authorship.md
|
|
19
|
+
- ../../../../clients/nycdoe/features/asn-sftp-ingestion-2-0.md
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
`_Model_Client_Case::_status` is DERIVED (never set) and stage writes via api2 are gated by a `case-set-stage` Surface rule; open before writing Cases from code, an integration, or the API.
|
|
23
|
+
|
|
24
|
+
## Summary
|
|
25
|
+
|
|
26
|
+
Stages are set; statuses are derived. A case's `_status` slug comes from its stage, else its owner. Moving a case to a blocker stage needs a stored reason note in the same request. Built for TOGa Desk Case Blocker (TRUE-82615, Desk ADR 0144). Shared core: applies to every 2.0 client. **On `_underscore` branch `TRUE-82615` (toga25 line) as of 2026-10-15 — not on `_production`.**
|
|
27
|
+
|
|
28
|
+
## How it works
|
|
29
|
+
|
|
30
|
+
- **`_status`** (plain `FIELD_SQL`, text slug): stage's linked `CaseStatuses.slug` if `caseStageId` set → else `ownerUserId` set → `in-progress` → else `open`. Same rule as `_Model_Client_Ticket::_status`. Never add `FIELDOPT_SQL_TYPE` (a cast zeroes the slug).
|
|
31
|
+
- **Blocker stages** (`roadblocked`, `on-hold`, `awaiting-user`) are `CaseStages` rows keyed by the new `CaseStages.slug`. Resume = set `caseStageId` to NULL (status falls back to owner/open).
|
|
32
|
+
- **`Client.CaseNotes` / `_Model_Client_CaseNote`**: write-once reason notes (`caseId`, `caseStageId`, `note`). `authorUserId` is stamped in `::save()` from `_Model::getRequestUserId()`, never from the payload. ACL: create + read only.
|
|
33
|
+
- **Set-stage gate**: Case `prePut`/`prePatch` capture the before-stage; `postPut`/`postPatch`/`postPost` read the `case-set-stage` Surface `requiredRule` for the new stage slug and its status slug. If the rule needs a reason, a `CaseNotes` row for this case + stage must have been stored in this request (`CaseNote::countCreatedFor`) — else `_Exception_Validation` (400) and the whole request rolls back. Wired by 5 `Core.ApiPayloadInterceptors` rows on cases (recordId 222).
|
|
34
|
+
- **`Ticket::createCase`** (`{"createCase":{}}` on PUT /tickets/{uuid}) now creates the case with **no stage** (it used to force the Open stage), so status derives.
|
|
35
|
+
- Central ids: `Core.Records` 377 (case-notes); `Core.RecordFields` 2743-2750. SQL: dbchanges2 `Client/2026-10-14b`, `Client/2026-10-14c`, `Core/2026-10-14b`, `Client/2026-10-15a` (case-notes ACL), `Client/2026-10-15b` (cases-stages read ACL).
|
|
36
|
+
|
|
37
|
+
## Gotchas
|
|
38
|
+
|
|
39
|
+
- **Do not write `_status` or pick an "Open" stage to mean open.** Leave `caseStageId` NULL; status derives.
|
|
40
|
+
- **Direct `_Model` writes skip the gate** (interceptors run only in api2). Nycdoe SFTP `Parse.php` creates cases this way; its cases now read `open` / `in-progress`.
|
|
41
|
+
- **PUT/PATCH a case without a route uuid → 400** — the gate needs `$api->recordUuid`.
|
|
42
|
+
- **Keep Cases FK child policies `MATCH`.** `MATCH_UPDATE` / `UPSERT` / `CREATE` write a case through a parent's FK object and bypass the gate.
|
|
43
|
+
- **The reason must be a stored row, not payload text** — api2 can swallow a failed nested note and still return 200.
|
|
44
|
+
|
|
45
|
+
## Change history
|
|
46
|
+
- 2026-10-15 — Created: derived case status, blocker stages, CaseNotes, set-stage gate, createCase without stage (TRUE-82615). (jcardinal)
|
|
@@ -6,8 +6,8 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: [apeterson]
|
|
9
|
+
updated: 2026-10-15
|
|
10
|
+
owners: [apeterson, jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Model/Client/SalesOrderNote.php
|
|
13
13
|
- _underscore/Model/Client/PurchaseOrderNote.php
|
|
@@ -18,6 +18,7 @@ files:
|
|
|
18
18
|
- _underscore/Model/Client/PartRequestNote.php
|
|
19
19
|
related:
|
|
20
20
|
- ../../toga-blox/features/add-notes-template.md
|
|
21
|
+
- ./case-status-and-stage-gate.md
|
|
21
22
|
- ../../toga25-supply/features/transfer-orders-page.md
|
|
22
23
|
- ../features/sales-order-denial-reason.md
|
|
23
24
|
- ../../../clients/compass-usa/features/approval-decision-flow.md
|
|
@@ -62,8 +63,12 @@ Before building anything that shows or filters by note author:
|
|
|
62
63
|
|
|
63
64
|
- **`contactId` on a `*Notes` model is an unwritten FK — treat it as always NULL.** Seven models, zero writers.
|
|
64
65
|
- **A notes list UI has no author to show.** Design for it, or the field silently renders blank for every row.
|
|
66
|
+
- **Exception — `CaseNotes` (2026-10-15) has a real author:** `authorUserId` stamped in `_Model_Client_CaseNote::save()` from `_Model::getRequestUserId()`. Copy that pattern for the real fix. See [Case status + stage gate](./case-status-and-stage-gate.md).
|
|
65
67
|
- **`dtStamp` is `FIELD_DATETIME_CREATED`** — stamped by the ORM on insert, the only provenance a note actually carries.
|
|
66
68
|
|
|
69
|
+
## Change history
|
|
70
|
+
- 2026-10-15 — Noted CaseNotes as the first note model with a server-stamped author. (jcardinal)
|
|
71
|
+
|
|
67
72
|
## Related
|
|
68
73
|
|
|
69
74
|
- [Add Notes template](../../toga-blox/features/add-notes-template.md)
|
|
@@ -6,8 +6,8 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-10-
|
|
10
|
-
owners: [dfranks]
|
|
9
|
+
updated: 2026-10-15
|
|
10
|
+
owners: [dfranks, jcardinal]
|
|
11
11
|
files: []
|
|
12
12
|
related:
|
|
13
13
|
- ../../../../standalone/apps/claude/workflows/clickup-ticket-workflow.md
|
|
@@ -24,7 +24,7 @@ How ClickUp surfaces branches/PRs/commits in a ticket's GitHub tab and the branc
|
|
|
24
24
|
|
|
25
25
|
- ClickUp's **native GitHub integration** auto-surfaces a branch, PR, or commit in a ticket's GitHub tab **whenever the ticket id appears in the branch name and/or the PR title/body**. Match is on the textual ticket id (e.g. `TRUE-79868`).
|
|
26
26
|
- **No ClickUp API call creates this link.** Linking is purely a side effect of the ticket id appearing in Git metadata.
|
|
27
|
-
- Durable rule for ticket work: name the **git branch as the BARE ticket id** (e.g. `TRUE-79868`), **one branch per repo**, PR title starts with the ticket id (`TRUE-79868 - <subject>`). PR base
|
|
27
|
+
- Durable rule for ticket work: name the **git branch as the BARE ticket id** (e.g. `TRUE-79868`), **one branch per repo**, PR title starts with the ticket id (`TRUE-79868 - <subject>`). PR base is set by the ticket's project — see `rules/common/git-workflow.md` → *Base branch*. Confirmed working 2026-10-08.
|
|
28
28
|
|
|
29
29
|
## Gotchas
|
|
30
30
|
|
|
@@ -38,6 +38,7 @@ How ClickUp surfaces branches/PRs/commits in a ticket's GitHub tab and the branc
|
|
|
38
38
|
|
|
39
39
|
## Change history
|
|
40
40
|
|
|
41
|
+
- 2026-10-15 — PR base now follows the ticket's project (Desk/IQ → `toga25`); links to git-workflow.md instead of restating. (jcardinal)
|
|
41
42
|
- 2026-10-08 — Added reconnect fix, Development-space repo linking, and settings to leave alone. (jcardinal)
|
|
42
43
|
- 2026-10-08 — `/work-ticket` and `/plan-ticket` retired; `/finish` opens the PRs. PR title = ticket id first; base = repo default branch; git-workflow now uses bare ticket-id branches. (jcardinal)
|
|
43
44
|
|
package/knowledge/INDEX.md
CHANGED
|
@@ -21,7 +21,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
21
21
|
|
|
22
22
|
## 2.0 framework
|
|
23
23
|
|
|
24
|
-
- **_underscore** (_Underscore) _(framework core)_ —
|
|
24
|
+
- **_underscore** (_Underscore) _(framework core)_ — 96 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
25
25
|
- **worker2** (Worker) — 86 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
26
26
|
- **api2** (API) — 26 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
27
27
|
- **dbchanges2** (Database Changes) _(framework core)_ — 22 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
|
|
@@ -6,8 +6,8 @@ project: Worker
|
|
|
6
6
|
client: nycdoe
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-10-
|
|
10
|
-
owners: [mhammontree]
|
|
9
|
+
updated: 2026-10-15
|
|
10
|
+
owners: [mhammontree, jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Sftp/Import.php
|
|
13
13
|
- worker2/Worker/Sftp/Parse.php
|
|
@@ -23,6 +23,7 @@ related:
|
|
|
23
23
|
- ../profile.md
|
|
24
24
|
- ../../../2.0/apps/worker2/features/codeception-integration-tests.md
|
|
25
25
|
- ../../../2.0/apps/_underscore/features/cli-script-bootstrap.md
|
|
26
|
+
- ../../../2.0/apps/_underscore/features/case-status-and-stage-gate.md
|
|
26
27
|
---
|
|
27
28
|
|
|
28
29
|
2.0 rewrite of DOE's vendor-SFTP ASN intake (TRUE-80519 Phases 1–2): per-manufacturer fetch to `Files`, header-validated parse into one Case per customer PO + one ServiceRequest per line; open for any 2.0 DOE ASN/SFTP work.
|
|
@@ -112,8 +113,10 @@ PRs open 2026-10-06: library, worker2, dbchanges2, _underscore (worker optional
|
|
|
112
113
|
- **`connect()` must not chain the phpseclib exception** — phpseclib `login()` frames carry the first 15 chars of the password, and worker2 stores job traces in `Core.WorkerJobs`. It rethrows a fresh exception with no `previous`.
|
|
113
114
|
- **Locks and just-committed rows must read the writer** — see the read-host gotcha in [Per-Client Database Connections](../../../2.0/apps/_underscore/features/per-client-database-connections.md).
|
|
114
115
|
- **Schema drift: `_Model_Client_Case` declares `longDescription` but `Client_Nycdoe.Cases` lacked the column** → any direct-model Case load/save 1054s. Fixed for DOE (Client_Nycdoe schema file). Other tenants' `Cases` may have the same gap — unchecked (ask Rohan).
|
|
116
|
+
- **Parsed Cases get no stage; `_status` is derived** (`open`, or `in-progress` once an owner is set) since TRUE-82615. Direct model writes skip the api2 set-stage gate. Never set a stage to mean "open" — see [Case status + stage gate](../../../2.0/apps/_underscore/features/case-status-and-stage-gate.md).
|
|
115
117
|
|
|
116
118
|
## Change history
|
|
119
|
+
- 2026-10-15 — Gotcha: parsed Cases now derive status (no stage) after the shared Case model change. (jcardinal)
|
|
117
120
|
- 2026-10-06 — 9 dbchanges2 files merged into one Client_Nycdoe + one Core file; runbook Part A updated (mhammontree)
|
|
118
121
|
- 2026-10-06 — Activation/deployment runbook (Part A inert, Part B shadow mode, rollback, failed-file release) (mhammontree)
|
|
119
122
|
- 2026-10-06 — Gated delete built (RunForFile/DeleteSource, Parse claim + status-outside-transaction fix, retired names, `released` status); open cutover items (mhammontree)
|
|
@@ -6,7 +6,7 @@ project: Claude Harness
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-10-
|
|
9
|
+
updated: 2026-10-15
|
|
10
10
|
owners: ["jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- claude/skills/start/SKILL.md
|
|
@@ -44,10 +44,11 @@ writes, and agents until the session has a ticket. These replaced `/kickoff`, `/
|
|
|
44
44
|
exists (rework: same branch, same PR). If ClickUp can't be read, it stops — no offline mode.
|
|
45
45
|
2. **`/commit`** — commits only this session's files, ticket from the session file, then the
|
|
46
46
|
branch. Then pushes the ticket branch (`git push -u origin <TICKET>`, never force).
|
|
47
|
-
3. **`/pseudocode`** — major tickets only. Writes the agreed
|
|
48
|
-
|
|
49
|
-
4. **`/finish`** — always writes the team KB first. Then
|
|
50
|
-
(Application, Stakeholders, Task Type, title, Has UI Changes)
|
|
47
|
+
3. **`/pseudocode`** — major tickets only. Writes the approach agreed with the developer to the
|
|
48
|
+
Pseudocode field — no separate write approval. Touches no other field.
|
|
49
|
+
4. **`/finish`** — always writes the team KB first. Then Claude decides and writes the ticket
|
|
50
|
+
fields itself in one call (Application, Stakeholders, Task Type, title, Has UI Changes) and
|
|
51
|
+
reports before → after — no approval. Then it asks "Is this ticket finished?" — a yes is the permission (no extra asks): commit any
|
|
51
52
|
uncommitted session files, push each
|
|
52
53
|
repo with work, open or update a PR per repo, post a PM comment, set status
|
|
53
54
|
`development finished`. The PM comment is plain bullets for a non-technical PM: "What
|
|
@@ -72,18 +73,18 @@ writes, and agents until the session has a ticket. These replaced `/kickoff`, `/
|
|
|
72
73
|
### Status flow
|
|
73
74
|
|
|
74
75
|
`to do` -> `in progress` (`/start`, only from `to do`, never backward) -> `development finished`
|
|
75
|
-
(`/finish`).
|
|
76
|
+
(`/finish`). Since 2026-10-15 the `start-gate` guard allows any status on the session ticket.
|
|
76
77
|
|
|
77
78
|
### Ticket-write rules
|
|
78
79
|
|
|
79
|
-
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
- **No approval for ticket writes (team rule, 2026-10-15).** Claude updates the session
|
|
81
|
+
ticket's fields, name, status, description, tags, priority, assignees and comments itself,
|
|
82
|
+
then reports before → after.
|
|
83
|
+
- The `start-gate` hook keeps two hard limits: writes go only to the session's own ticket (any
|
|
84
|
+
other task id is blocked), and never to Has Code Changes (worker2 owns it). It also blocks task
|
|
85
|
+
creation, deletion, moves, links, docs, chat, time tracking and `update_comment` (no task id
|
|
86
|
+
to check).
|
|
87
|
+
- No permission prompt for `clickup_update_task` or `clickup_create_comment` (since 2026-10-09). Only `Bash(git push *)` stays in `permissions.ask`. `npx toga-ai`
|
|
87
88
|
strips the old ClickUp rules from a dev's existing `permissions.ask` (see harness distribution).
|
|
88
89
|
- Use only the `clickup` MCP server (`mcp__clickup__clickup_*`). The old read-only
|
|
89
90
|
"claude.ai ClickUP for Dev" server (`mcp__claude_ai_ClickUP_for_Dev__*`) is blocked.
|
|
@@ -134,8 +135,9 @@ All tested 2026-10-08:
|
|
|
134
135
|
.scss .less .html`, a React app's `src/`, blox components), else unchecks it.
|
|
135
136
|
- **Branch = bare ticket id**, created by ClickUp or by `/start`'s `create-branch`. A branch
|
|
136
137
|
named with the ticket id should link to the ticket through the GitHub integration — confirm on
|
|
137
|
-
the first real `create-branch` run.
|
|
138
|
-
`
|
|
138
|
+
the first real `create-branch` run. Base branch is set by the ticket's project (TOGa Desk / TOGa IQ ticket → every repo from
|
|
139
|
+
`toga25`; else each repo's default branch) — rule lives in `rules/common/git-workflow.md`
|
|
140
|
+
→ *Base branch*.
|
|
139
141
|
- **The KB repo (`toga-tech`) works on `_main`.** No branch check and no PRs there; `/finish`
|
|
140
142
|
still does the ticket check.
|
|
141
143
|
- **Re-check the branch, never repeat it.** When the developer says they switched (or before
|
|
@@ -157,6 +159,10 @@ All tested 2026-10-08:
|
|
|
157
159
|
|
|
158
160
|
## Change history
|
|
159
161
|
|
|
162
|
+
- 2026-10-15 — Ticket writes need no approval: `/finish` writes fields itself, `/pseudocode`
|
|
163
|
+
writes once the plan is agreed; the gate allows any field/status/tag/comment on the session
|
|
164
|
+
ticket, except Has Code Changes. (jcardinal)
|
|
165
|
+
- 2026-10-15 — Base branch now follows the ticket's project (Desk/IQ → `toga25`); points at git-workflow.md. (jcardinal)
|
|
160
166
|
- 2026-10-08 — Created: ClickUp-tied sessions (`/start`, `/commit`, `/pseudocode`, `/finish`)
|
|
161
167
|
replace `/kickoff`, `/capture`, `/plan-ticket`, `/work-ticket`, `/rework-ticket`. (jcardinal)
|
|
162
168
|
- 2026-10-08 — Branch claims always re-checked fresh; `/finish` runs `/commit` on yes and
|
package/package.json
CHANGED
|
@@ -48,9 +48,28 @@ What you need to do:
|
|
|
48
48
|
1. Run `2026-08-31a - AddColumn.sql` on the Core database (prod).
|
|
49
49
|
2. Then run `2026-08-31b - Seed.sql` on the same database.
|
|
50
50
|
3. Deploy worker2 to Elastic Beanstalk.
|
|
51
|
-
4.
|
|
51
|
+
4. Tell me when done — I check the new column myself.
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
### Database changes: give a SQL file, not a description (team rule, 2026-10-15)
|
|
55
|
+
|
|
56
|
+
Applies to every repo. When a developer must change data or schema — ACL grant rows, seeds,
|
|
57
|
+
data cleanup, anything — Claude delivers a **ready-to-run SQL file**, never a prose list of
|
|
58
|
+
rows for the developer to write by hand.
|
|
59
|
+
|
|
60
|
+
1. **Put the file in the right folder:** `dbchanges2` for 2.0, `dbchanges` for 1.0. Follow the
|
|
61
|
+
existing SQL file rules (file name with today's date, `ADD COLUMN ... AFTER`, random v4 uuids,
|
|
62
|
+
guarded re-runnable seeds, one database family per query — see the `dbchanges2`
|
|
63
|
+
architecture doc and `security.md`).
|
|
64
|
+
2. **Add a "What you need to do" numbered run list** (format above): which file, which
|
|
65
|
+
database, in what order. For a `dbchanges2` `Client/` file the step always says
|
|
66
|
+
**"every client DB"**.
|
|
67
|
+
3. **ACL grants stay the developer's call.** Claude proposes the roles in the file header
|
|
68
|
+
comment; the developer decides before running.
|
|
69
|
+
4. **Put verify SELECTs at the end of the file.** After the developer runs it, Claude runs
|
|
70
|
+
those SELECTs itself (local DB, or `toga-db` read-only) and reports the result in plain
|
|
71
|
+
words. Never ask the developer to read verify output.
|
|
72
|
+
|
|
54
73
|
## Cut output tokens — the safe way
|
|
55
74
|
|
|
56
75
|
Shorter answers are clearer AND cheaper to run. Both matter. But **clarity always wins** —
|
|
@@ -16,11 +16,14 @@ the commit prefix, the PR title, and the ticket updates at `/finish`.
|
|
|
16
16
|
2. **`/finish`** does the KB capture, then asks one question: **"Is this ticket finished?"**
|
|
17
17
|
On yes it: commits any uncommitted session files, pushes, opens (or updates) one PR per
|
|
18
18
|
repo, comments on the ticket, and sets the status. No extra "may I push?" or "may I open
|
|
19
|
-
the PR?" questions — that yes is the permission.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
the PR?" questions — that yes is the permission.
|
|
20
|
+
3. **ClickUp ticket writes need no approval** (decided 2026-10-15, jcardinal). Claude updates
|
|
21
|
+
the session ticket's fields, title, status and comments itself, then reports before → after.
|
|
22
|
+
Only the session's own ticket, and never Has Code Changes (worker2 owns it) — the
|
|
23
|
+
`start-gate` hook enforces both.
|
|
24
|
+
4. **Branches.** Claude may create or switch to the ticket branch as part of normal work.
|
|
25
|
+
5. `/ship`, `/land-and-deploy` — the commits, pushes, and PRs they describe.
|
|
26
|
+
6. The KB repo (`~/toga-tech`) — `/finish` commits and pushes to `_main`.
|
|
24
27
|
|
|
25
28
|
Always, in every repo:
|
|
26
29
|
|
|
@@ -47,7 +50,7 @@ either (see *Claude never rewrites code because a branch changed*). Present diff
|
|
|
47
50
|
let the developer commit, PR, and deploy.
|
|
48
51
|
|
|
49
52
|
**One exception — `/start`'s `create-branch`.** At `/start`, after the developer says yes for
|
|
50
|
-
that repo, Claude may run `start-helper.js create-branch` to create (from the
|
|
53
|
+
that repo, Claude may run `start-helper.js create-branch` to create (from the ticket's base branch, see *Branch naming*)
|
|
51
54
|
or check out the ticket branch `<TICKET>`. Nothing else: no worktree, no stash, never on a
|
|
52
55
|
repo with uncommitted changes, never force-push. See *Branch naming*.
|
|
53
56
|
|
|
@@ -99,14 +102,23 @@ Never force-push to `_main` under any circumstances.
|
|
|
99
102
|
The branch is the bare ClickUp ticket id — e.g. `TRUE-82600`. No prefix, no description.
|
|
100
103
|
ClickUp may create it, or `/start` creates it. One branch per ticket per repo.
|
|
101
104
|
|
|
102
|
-
**Base branch.**
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
**Base branch.** The ticket's project (its Application) decides the base — not the repo.
|
|
106
|
+
|
|
107
|
+
- **New platform app ticket** (today TOGa Desk = `toga25-desk`, TOGa IQ = `toga25-iq`): every repo
|
|
108
|
+
touched for that ticket — the app and its dependencies (`toga25-blox`, `toga2-template`,
|
|
109
|
+
`_underscore`, `api2`, `dbchanges2`, `worker2`, …) — branches from `toga25`. Its PR goes back to
|
|
110
|
+
`toga25`. At `/start`, pass `--base toga25` to `create-branch`.
|
|
111
|
+
- **Any other project** (e.g. TOGa Commerce, TOGa Supply, Worker): every repo branches from its own
|
|
112
|
+
default branch (e.g. `_production`). PRs go there — even when the ticket touches the same shared
|
|
113
|
+
repos (`_underscore`, `api2`, …).
|
|
114
|
+
- Example: a `toga25-desk` ticket touching `_underscore` → `_underscore` branches from `toga25`. A
|
|
115
|
+
`toga2-commerce` ticket touching `_underscore` → from `_production`.
|
|
116
|
+
- The KB repo `toga-tech` is unchanged: `_main`, no branch, no PR.
|
|
105
117
|
|
|
106
118
|
`/start` checks that every repo on the ticket (from its Application values) is on the ticket
|
|
107
119
|
branch. On a mismatch it asks once per repo: "Create/switch to `<TICKET>` in `<repo>` from
|
|
108
120
|
`<base>`?" On yes it runs `start-helper.js create-branch` (checks out `origin/<TICKET>` or the
|
|
109
|
-
local branch if one exists, else creates it from the
|
|
121
|
+
local branch if one exists, else creates it from the base branch above and pushes it with
|
|
110
122
|
`git push -u`, never force). A repo with uncommitted changes is refused — Claude stops and
|
|
111
123
|
says so. Claude may also create or switch to the ticket branch later in normal work, with the
|
|
112
124
|
same limits.
|
package/rules/common/security.md
CHANGED
|
@@ -75,6 +75,9 @@ escaping, and it will corrupt data you meant to store intact.
|
|
|
75
75
|
|
|
76
76
|
## Never query across database clusters (2.0)
|
|
77
77
|
|
|
78
|
+
(Handing a DB change to a developer? Give a SQL file — see `communication.md` § *Database
|
|
79
|
+
changes: give a SQL file, not a description*.)
|
|
80
|
+
|
|
78
81
|
In 2.0 the database families live on **separate production clusters**: `Core` / `Forecast` /
|
|
79
82
|
`Team` (prod-core), `Client_<Tenant>` (prod-client), `Archive_<Tenant>` (prod-archive), and
|
|
80
83
|
`Logs` / `Logs_<Tenant>` (prod-logs). A single SQL statement that joins or sub-selects across
|
|
@@ -33,11 +33,18 @@
|
|
|
33
33
|
* the team repo.
|
|
34
34
|
*
|
|
35
35
|
* ClickUp guard (any stage, any tool name containing "clickup"): an ALLOWLIST. Reads pass.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* `
|
|
39
|
-
*
|
|
40
|
-
*
|
|
36
|
+
* Team rule 2026-10-15 (jcardinal): Claude may update the SESSION ticket's fields, name, status,
|
|
37
|
+
* description, tags, priority, assignees and comments without asking. So on the session ticket
|
|
38
|
+
* these writes pass: `update_task` (keys in UPDATE_TASK_KEYS, any status, any custom field),
|
|
39
|
+
* `add_tag_to_task` / `remove_tag_from_task`, and `create_comment` (entity_type "task") /
|
|
40
|
+
* `create_task_comment`. Two guards stay:
|
|
41
|
+
* 1. Session ticket only — never another task. A wrong id would silently change someone
|
|
42
|
+
* else's ticket, and the task id is the only thing tying a write to this session.
|
|
43
|
+
* 2. Never Has Code Changes — worker2 sets it from the PRs; a Claude write would fight it.
|
|
44
|
+
* Everything else is blocked (create/delete/move tasks, links, docs, chat, time tracking,
|
|
45
|
+
* `update_comment` — it carries no task id, so the ticket cannot be checked), including every
|
|
46
|
+
* tool of the old "ClickUP for Dev" server. This branch FAILS CLOSED on an internal error; the
|
|
47
|
+
* rest of the gate fails open.
|
|
41
48
|
*
|
|
42
49
|
* Subagents: hook input carries `agent_id` (only inside a subagent) and `agent_type`. The
|
|
43
50
|
* ticket-reader subagent may run Bash only as `node <pinned>/ticket-parse.js ...`.
|
|
@@ -101,17 +108,15 @@ const MAX_UNWRAP_DEPTH = 6;
|
|
|
101
108
|
const OLD_CLICKUP_SERVER_RE = /^claude_ai_ClickUP_for_Dev$/i;
|
|
102
109
|
/* get_* covers get_operators and get_schema. download_* is a read but is not needed — blocked. */
|
|
103
110
|
const CLICKUP_READ_RE = /^(get_.+|search|search_.+|filter_.+|list_.+|find_.+|resolve_assignees)$/;
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const WRITABLE_FIELDS = new Map([
|
|
109
|
-
['32e9fcd3-6f59-448a-a06e-de52afc87587', 'Application'],
|
|
110
|
-
['ec15edfd-5920-4c12-a1dc-94ede4a62438', 'Stakeholders'],
|
|
111
|
-
['093170c3-abdc-4815-9f1f-f51841799590', 'Task Type'],
|
|
112
|
-
['ad0e4fc8-ff03-4e5f-a187-7d2cd6d0bc65', 'Pseudocode'],
|
|
113
|
-
['576ff7b7-a2bf-4e78-bd55-85661d1a98ef', 'Has UI Changes'],
|
|
111
|
+
/* Any field of the session ticket (team rule 2026-10-15). Not `task_type`: Task Type is a custom field here. */
|
|
112
|
+
const UPDATE_TASK_KEYS = new Set([
|
|
113
|
+
'task_id', 'workspace_id', 'status', 'name', 'custom_fields', 'markdown_description', 'priority',
|
|
114
|
+
'assignees', 'due_date', 'start_date', 'time_estimate',
|
|
114
115
|
]);
|
|
116
|
+
const CUSTOM_FIELD_ITEM_KEYS = new Set(['id', 'value']);
|
|
117
|
+
/* Writes on the session ticket keyed by task_id. */
|
|
118
|
+
const TASK_ID_WRITE_OPS = new Set(['update_task', 'add_tag_to_task', 'remove_tag_from_task', 'create_task_comment']);
|
|
119
|
+
/* worker2 owns it (sets it from the ticket's PRs) — Claude never writes it. */
|
|
115
120
|
const HAS_CODE_CHANGES_FIELD = 'af9eaba8-d3d3-4769-b8bc-28de78b04570';
|
|
116
121
|
|
|
117
122
|
/* ---------------- shell allowlist data ---------------- */
|
|
@@ -186,10 +191,10 @@ const MSG = {
|
|
|
186
191
|
noSessionTicket: 'ClickUp write blocked: this session has no ticket. Run /start TRUE-1234 first.',
|
|
187
192
|
alwaysBlocked: (op) => 'ClickUp call blocked: `' + op + '` is not allowed from Claude. Ask the developer to do it in ClickUp.',
|
|
188
193
|
commentEntity: 'ClickUp write blocked: comments may only go on the session ticket (entity_type "task").',
|
|
189
|
-
badKey: (k) => 'ClickUp write blocked: update_task may not set `' + k + '`. Allowed:
|
|
190
|
-
badStatus: 'ClickUp write blocked:
|
|
194
|
+
badKey: (k) => 'ClickUp write blocked: update_task may not set `' + k + '`. Allowed: ' + [...UPDATE_TASK_KEYS].filter((x) => x !== 'task_id' && x !== 'workspace_id').join(', ') + '.',
|
|
195
|
+
badStatus: 'ClickUp write blocked: status must be a non-empty string.',
|
|
191
196
|
hasCodeChanges: 'ClickUp write blocked: never write Has Code Changes — worker2 owns it.',
|
|
192
|
-
badField: (id) => 'ClickUp write blocked: custom field `' + id + '`
|
|
197
|
+
badField: (id) => 'ClickUp write blocked: custom field item `' + id + '` must be {id, value}.',
|
|
193
198
|
guardError: 'ClickUp call blocked: the start gate could not check it (internal error). Try again; if it repeats, tell the developer.',
|
|
194
199
|
};
|
|
195
200
|
|
|
@@ -762,8 +767,8 @@ function checkCustomFields(value) {
|
|
|
762
767
|
for (const item of list) {
|
|
763
768
|
if (!item || typeof item !== 'object' || Array.isArray(item)) return MSG.badField(JSON.stringify(item));
|
|
764
769
|
const id = String(item.id || '');
|
|
765
|
-
if (id === HAS_CODE_CHANGES_FIELD) return MSG.hasCodeChanges;
|
|
766
|
-
if (!
|
|
770
|
+
if (id.trim().toLowerCase() === HAS_CODE_CHANGES_FIELD) return MSG.hasCodeChanges;
|
|
771
|
+
if (!id) return MSG.badField('no id');
|
|
767
772
|
const extra = Object.keys(item).find((k) => !CUSTOM_FIELD_ITEM_KEYS.has(k));
|
|
768
773
|
if (extra) return MSG.badKey('custom_fields[].' + extra);
|
|
769
774
|
}
|
|
@@ -772,7 +777,7 @@ function checkCustomFields(value) {
|
|
|
772
777
|
|
|
773
778
|
function checkUpdateTask(ti) {
|
|
774
779
|
for (const k of Object.keys(ti)) if (!UPDATE_TASK_KEYS.has(k)) return MSG.badKey(k);
|
|
775
|
-
if ('status' in ti && (typeof ti.status !== 'string' || !
|
|
780
|
+
if ('status' in ti && (typeof ti.status !== 'string' || !ti.status.trim())) return MSG.badStatus;
|
|
776
781
|
if ('name' in ti && typeof ti.name !== 'string') return MSG.badKey('name');
|
|
777
782
|
if ('custom_fields' in ti) return checkCustomFields(ti.custom_fields);
|
|
778
783
|
return null;
|
|
@@ -786,7 +791,7 @@ function clickupVerdict(tool, toolInput, sid) {
|
|
|
786
791
|
const op = m[2].replace(/^clickup_/i, '').toLowerCase();
|
|
787
792
|
if (OLD_CLICKUP_SERVER_RE.test(server)) return MSG.oldClickup;
|
|
788
793
|
if (CLICKUP_READ_RE.test(op)) return null;
|
|
789
|
-
if (op !== '
|
|
794
|
+
if (op !== 'create_comment' && !TASK_ID_WRITE_OPS.has(op)) return MSG.alwaysBlocked(m[2]);
|
|
790
795
|
if (server !== CLICKUP_SERVER) return MSG.otherServer(server);
|
|
791
796
|
const ti = toolInput && typeof toolInput === 'object' && !Array.isArray(toolInput) ? toolInput : {};
|
|
792
797
|
const session = sid ? readSession(sid) : null;
|
|
@@ -796,7 +801,7 @@ function clickupVerdict(tool, toolInput, sid) {
|
|
|
796
801
|
return sameTicket(ti.entity_id || '', session) ? null : MSG.otherTicket(String(ti.entity_id || 'no task id'));
|
|
797
802
|
}
|
|
798
803
|
if (!sameTicket(ti.task_id || '', session)) return MSG.otherTicket(String(ti.task_id || 'no task id'));
|
|
799
|
-
return checkUpdateTask(ti);
|
|
804
|
+
return op === 'update_task' ? checkUpdateTask(ti) : null;
|
|
800
805
|
}
|
|
801
806
|
|
|
802
807
|
function clickupGuard(tool, toolInput, sid) {
|
package/skills/finish/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: finish
|
|
3
|
-
description: End-of-session closer for TOGA Technology work tied to a ClickUp ticket (replaces /capture). Always records the session's durable knowledge into the team `claude` knowledge base first (search, classify CREATE/UPDATE/DEPRECATE/NO-OP, write, publish to `_main`). Then checks the session ticket's fields (Application, Stakeholders, Task Type, title, Has UI Changes) in
|
|
3
|
+
description: End-of-session closer for TOGA Technology work tied to a ClickUp ticket (replaces /capture). Always records the session's durable knowledge into the team `claude` knowledge base first (search, classify CREATE/UPDATE/DEPRECATE/NO-OP, write, publish to `_main`). Then checks the session ticket's fields (Application, Stakeholders, Task Type, title, Has UI Changes) and updates them itself in one call (no approval needed), asks "Is this ticket finished?", and if yes pushes the ticket branch in every repo with work, opens or updates one PR per repo (with the full review summary in the description), posts a short PM comment, and sets status `development finished`. Run at the END of EVERY session, finished or not. Trigger on "/finish", "finish", "wrap up", "capture", "save my work to the knowledge base", "I'm done", or when the developer ends a session.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# /finish — save the knowledge, check the ticket, ship the ticket
|
|
@@ -12,8 +12,9 @@ Keep the main thread lean: the KB write pipeline runs in the `session-capture` s
|
|
|
12
12
|
ClickUp reads run in the `ticket-reader` subagent. The main thread distills and asks only for real decisions.
|
|
13
13
|
|
|
14
14
|
Claude writes and posts all summary text itself — PM comment, PR subject and body, commit
|
|
15
|
-
notes, KB capture summary. Never ask the developer to approve that wording.
|
|
16
|
-
|
|
15
|
+
notes, KB capture summary. Never ask the developer to approve that wording. Claude also
|
|
16
|
+
updates the session ticket's fields, status and comments itself — no approval (team rule,
|
|
17
|
+
2026-10-15). Approvals are only for real decisions: ELEVATED KB docs and "Is this ticket finished?". A yes to
|
|
17
18
|
that question is the permission to commit, push, and open the PRs — no extra asks.
|
|
18
19
|
|
|
19
20
|
## Step 0 — Rules, helpers, session ticket
|
|
@@ -27,18 +28,18 @@ exactly these, nothing else):
|
|
|
27
28
|
origin _main` (never force, no PR) — the KB repo exception in `rules/common/git-workflow.md`.
|
|
28
29
|
4. `gh pr create` for those repos, or `gh pr edit <n> --body-file <file>` to rebuild the body of an open PR.
|
|
29
30
|
5. ClickUp writes **on the session ticket only**, with exactly two tools:
|
|
30
|
-
- `mcp__clickup__clickup_update_task` —
|
|
31
|
-
|
|
32
|
-
Has UI Changes.
|
|
31
|
+
- `mcp__clickup__clickup_update_task` — any field, name, status, description, tags,
|
|
32
|
+
priority or assignees of the session ticket. /finish uses `name`, `status` = `development
|
|
33
|
+
finished`, and the custom fields Application, Stakeholders, Task Type, Has UI Changes.
|
|
33
34
|
- `mcp__clickup__clickup_create_comment` — one PM comment, `entity_type: "task"`.
|
|
34
35
|
|
|
35
36
|
Never: force-push, push any other branch, commit source code (that is `/commit`), write the
|
|
36
37
|
**Has Code Changes** field (`af9eaba8-d3d3-4769-b8bc-28de78b04570` — worker2 owns it), write
|
|
37
38
|
"Pseudocode Review" / "Pseudocode Required", touch any other ticket, or use the old read-only
|
|
38
39
|
`mcp__claude_ai_ClickUP_for_Dev__*` tools. The `start-gate` hook blocks every other ClickUp
|
|
39
|
-
write (other tools, other
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
write (other tools, other tickets, Has Code Changes). Field changes, status and the PM comment
|
|
41
|
+
need no approval — write them, then report them. `git push` may ask for permission — that is
|
|
42
|
+
expected.
|
|
42
43
|
|
|
43
44
|
**Helpers** (both print JSON):
|
|
44
45
|
|
|
@@ -241,7 +242,7 @@ validate → index → mirror → rebase-push command. The `PostToolUse` hook st
|
|
|
241
242
|
|
|
242
243
|
---
|
|
243
244
|
|
|
244
|
-
# Part 2 — Ticket field check (
|
|
245
|
+
# Part 2 — Ticket field check (Claude decides and writes — no approval)
|
|
245
246
|
|
|
246
247
|
Skip Part 2 and Part 3 if the session has no verified ticket (Step 0).
|
|
247
248
|
|
|
@@ -274,34 +275,34 @@ Compare the ticket (Step 0 read) with what the session actually did:
|
|
|
274
275
|
6. Never touch Has Code Changes.
|
|
275
276
|
|
|
276
277
|
**Labels REPLACE the whole set.** For Application and Stakeholders, the new value is
|
|
277
|
-
`current set ∪ additions − removals` — never just the additions.
|
|
278
|
-
|
|
278
|
+
`current set ∪ additions − removals` — never just the additions. Remove a value only when the
|
|
279
|
+
work clearly shows it is wrong, and give the reason in the report.
|
|
279
280
|
|
|
280
|
-
## Step 9 —
|
|
281
|
+
## Step 9 — Decide the changes (no approval)
|
|
281
282
|
|
|
282
|
-
|
|
283
|
+
Claude decides the changes itself. Do not ask the developer to approve them (team rule,
|
|
284
|
+
2026-10-15). Keep a list of only the fields that change, each with the reason. Example:
|
|
283
285
|
|
|
284
286
|
```
|
|
285
|
-
Ticket TRUE-82600 —
|
|
286
|
-
1. Application: add "TOGa Desk" (
|
|
287
|
-
2. Stakeholders: add "Compass Group" (
|
|
288
|
-
3. Task Type: "Bug" → "Enhancement"
|
|
287
|
+
Ticket TRUE-82600 — changes:
|
|
288
|
+
1. Application: add "TOGa Desk" (was: api2) → api2, TOGa Desk — work also changed the Desk app
|
|
289
|
+
2. Stakeholders: add "Compass Group" (was: none) — fix is for Compass
|
|
290
|
+
3. Task Type: "Bug" → "Enhancement" — new behavior, not a fix
|
|
289
291
|
4. Title: "Fix thing" → "Compass - Fix order-for dropdown on Desk [Claude modified]"
|
|
290
|
-
5. Has UI Changes: true → false
|
|
291
|
-
Approve all, some (give numbers), or none?
|
|
292
|
+
5. Has UI Changes: true → false — no UI files changed
|
|
292
293
|
```
|
|
293
294
|
|
|
294
295
|
No changes → say "Ticket fields look right — no changes." and go to Part 3.
|
|
295
296
|
|
|
296
|
-
## Step 10 — Write the
|
|
297
|
+
## Step 10 — Write the changes (one call), read back, report before → after
|
|
297
298
|
|
|
298
|
-
One `mcp__clickup__clickup_update_task` call with
|
|
299
|
+
One `mcp__clickup__clickup_update_task` call with all the changes from Step 9. Do **not** use the
|
|
299
300
|
tool's `task_type` parameter — Task Type here is a custom field.
|
|
300
301
|
|
|
301
302
|
```
|
|
302
303
|
mcp__clickup__clickup_update_task
|
|
303
304
|
task_id: "<TICKET>" (custom id, e.g. "TRUE-82600")
|
|
304
|
-
name: "<new title>" (only if
|
|
305
|
+
name: "<new title>" (only if the title changes)
|
|
305
306
|
custom_fields: [
|
|
306
307
|
{"id": "32e9fcd3-6f59-448a-a06e-de52afc87587", "value": "[\"<app uuid>\",\"<app uuid>\"]"},
|
|
307
308
|
{"id": "ec15edfd-5920-4c12-a1dc-94ede4a62438", "value": "[\"<client uuid>\"]"},
|
|
@@ -313,9 +314,9 @@ mcp__clickup__clickup_update_task
|
|
|
313
314
|
Value formats (all strings): labels = JSON array string of option UUIDs (the FULL set);
|
|
314
315
|
drop_down = one option UUID; checkbox = `"true"` / `"false"`.
|
|
315
316
|
|
|
316
|
-
Then spawn `ticket-reader` (`mode: ticket`) again and
|
|
317
|
-
|
|
318
|
-
comment.
|
|
317
|
+
Then spawn `ticket-reader` (`mode: ticket`) again and check each value stuck. If a value did
|
|
318
|
+
not stick, say so plainly. Keep the before → after list with reasons: it goes in the Step 17
|
|
319
|
+
report (and the "is this finished?" no-path report) and in the PM comment.
|
|
319
320
|
|
|
320
321
|
---
|
|
321
322
|
|
|
@@ -557,7 +558,7 @@ developer the list name and leave the status alone.
|
|
|
557
558
|
Short list:
|
|
558
559
|
|
|
559
560
|
1. KB: publish status (Step 5).
|
|
560
|
-
2. Ticket fields changed (or "none").
|
|
561
|
+
2. Ticket fields changed: before → after with the reason, one line each (or "none").
|
|
561
562
|
3. PRs: one url per repo, and which were created vs. updated.
|
|
562
563
|
4. PM comment: posted (yes/no), with the posted text.
|
|
563
564
|
5. Status: `development finished` (or why not).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pseudocode
|
|
3
|
-
description: Write the agreed approach for a MAJOR ClickUp ticket into the ticket's Pseudocode field, as clean plain text,
|
|
3
|
+
description: Write the agreed approach for a MAJOR ClickUp ticket into the ticket's Pseudocode field, as clean plain text, once the plan is agreed with the developer (no separate approval for the write). Writes the session ticket only, and only that one field. Trigger on "/pseudocode", "write the pseudocode", "put the plan on the ticket".
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# /pseudocode — put the agreed approach on the ticket
|
|
@@ -15,9 +15,9 @@ insists.
|
|
|
15
15
|
(`ad0e4fc8-ff03-4e5f-a187-7d2cd6d0bc65`). Never "Pseudocode Review", "Pseudocode Required",
|
|
16
16
|
or any other field.
|
|
17
17
|
- The only write is `mcp__clickup__clickup_update_task` with that one custom field. Never
|
|
18
|
-
`mcp__claude_ai_ClickUP_for_Dev__*`. The `start-gate` hook blocks any other
|
|
19
|
-
|
|
20
|
-
-
|
|
18
|
+
`mcp__claude_ai_ClickUP_for_Dev__*`. The `start-gate` hook blocks writes to any other ticket.
|
|
19
|
+
- The plan itself must be agreed with the developer. The write needs no extra approval (team
|
|
20
|
+
rule, 2026-10-15) — once the plan is agreed, write it and report.
|
|
21
21
|
|
|
22
22
|
## Step 1 — Session ticket
|
|
23
23
|
|
|
@@ -67,19 +67,20 @@ OUT OF SCOPE
|
|
|
67
67
|
1. Email notices.
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
## Step 4 —
|
|
70
|
+
## Step 4 — Check the plan is agreed
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
The text must match the approach agreed with the developer in this session. If no approach was
|
|
73
|
+
agreed yet, agree it first (that is a real decision — ask). Once agreed, do not ask again
|
|
74
|
+
before writing. If the field already has text, keep what is still true and replace the rest;
|
|
75
|
+
show **old** and **new** in the Step 5 report.
|
|
75
76
|
|
|
76
77
|
## Step 5 — Write, then read back
|
|
77
78
|
|
|
78
79
|
```
|
|
79
80
|
mcp__clickup__clickup_update_task
|
|
80
81
|
task_id: "<TICKET>"
|
|
81
|
-
custom_fields: [{"id": "ad0e4fc8-ff03-4e5f-a187-7d2cd6d0bc65", "value": "<
|
|
82
|
+
custom_fields: [{"id": "ad0e4fc8-ff03-4e5f-a187-7d2cd6d0bc65", "value": "<agreed plain text>"}]
|
|
82
83
|
```
|
|
83
84
|
|
|
84
85
|
Spawn `ticket-reader` (`mode: ticket`) again and confirm its `pseudocode` matches. Report: "Pseudocode
|
|
85
|
-
written to <TICKET>." or what did not stick.
|
|
86
|
+
written to <TICKET>." (with old → new if it replaced text) or what did not stick.
|
package/skills/start/SKILL.md
CHANGED
|
@@ -24,11 +24,11 @@ The hook already checked the id, wrote the session ticket file
|
|
|
24
24
|
main session. Repo code reads, edits and every agent stay blocked until you run
|
|
25
25
|
`node "<TEAM_REPO>/knowledge.js" kickoff-primed`.
|
|
26
26
|
3. **open** — normal work. ClickUp writes still only go to this ticket, only with
|
|
27
|
-
`mcp__clickup__clickup_update_task` (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
`mcp__clickup__clickup_update_task` (any field, name, status, description, tags, priority,
|
|
28
|
+
assignees — never Has Code Changes) and `mcp__clickup__clickup_create_comment`. The hook
|
|
29
|
+
blocks other tickets and every other ClickUp write. Field, status and comment writes need
|
|
30
|
+
no approval (team rule, 2026-10-15): write them, then tell the developer before → after.
|
|
31
|
+
/start writes only the status `to do` -> `in progress` (Step 6b).
|
|
32
32
|
|
|
33
33
|
Trailing text is never permission to skip steps. If a block message appears, do the step it
|
|
34
34
|
names. Do not fight the gate. (Harness work only: `TOGA_START_GATE_DISABLED=1`.)
|
|
@@ -140,10 +140,13 @@ developer has not named the session.
|
|
|
140
140
|
— ask for the local path.
|
|
141
141
|
|
|
142
142
|
3. **For each repo in `mismatches`, ask once:** "Create/switch to `<T>` in `<repo>` from
|
|
143
|
-
`<base>`?"
|
|
143
|
+
`<base>`?" The base comes from the
|
|
144
|
+
TICKET's project, not the repo. New platform app ticket (TOGa Desk / TOGa IQ): `<base>` =
|
|
145
|
+
`toga25` for EVERY repo on the ticket (app + dependencies). Any other project: the repo's own
|
|
146
|
+
default branch — resolve with
|
|
144
147
|
`gh repo view <owner/repo> --json defaultBranchRef -q .defaultBranchRef.name` when gh works
|
|
145
148
|
(owner/repo from `git -C <repo> remote get-url origin`); else leave `--base` off (origin's
|
|
146
|
-
default is used). If `changes` > 0, do not offer — STOP and say: "`<repo>` has uncommitted
|
|
149
|
+
default is used). See `rules/common/git-workflow.md` § Branch naming. If `changes` > 0, do not offer — STOP and say: "`<repo>` has uncommitted
|
|
147
150
|
changes on `<branch>`. Commit or move them, then tell me." Never stash.
|
|
148
151
|
|
|
149
152
|
4. **On yes:**
|
|
@@ -264,12 +267,14 @@ KB loaded: <doc titles>; gaps: <none | list>
|
|
|
264
267
|
|
|
265
268
|
State the permission policy in one line: "File reads and local edits run automatically;
|
|
266
269
|
anything that executes, deploys, hits a remote/production environment, or leaves this machine,
|
|
267
|
-
I'll confirm with you first.
|
|
270
|
+
I'll confirm with you first. I update this ticket's fields, status and comments myself and
|
|
271
|
+
tell you what changed."
|
|
268
272
|
|
|
269
273
|
## During the session
|
|
270
274
|
|
|
271
275
|
- If you notice another app or client is affected, or the title is far off, note it for
|
|
272
|
-
`/finish
|
|
276
|
+
`/finish` (it writes all field changes in one call). You may also update the ticket now
|
|
277
|
+
without asking — then tell the developer before → after in one line.
|
|
273
278
|
- Commit with `/commit`. `/finish` pushes, opens PRs, and updates the ticket.
|
|
274
279
|
- `/pseudocode` writes the agreed plan to the ticket (major tickets only).
|
|
275
280
|
|