Skip to content

Commit f08bfef

Browse files
Nick Websterclaude
andcommitted
feat(item): add monday item get-description — read Item.description (v0.11-M54-G pre-flight)
New narrow read verb that surfaces `Item.description { id, blocks: [DocumentBlock] }` via raw GraphQL at API 2026-01. Mirrors v0.9-M52's `board views <bid>` carve-out from `board describe`: heavy/nested content lives behind an opt-in verb rather than bloating ITEM_FIELDS_FRAGMENT's ~16 universal consumers (`item get` / `list` / `find` / `search` / `subitems` / `history` + every mutation result). Closes R-v0.9-NEW-8 (filed at v0.9-M52 close-docs; user-directed at the v0.11 candidate-selection 2026-05-22). Per R-v0.9-NEW-2 this is a pure-additive new verb with no deferred wire leg — the verb's wire leg IS the runtime payload at pre-flight, no stub literal needed (3rd structurally distinct instance of the rule after M50 deletion and M53 pure refactor). Empirical probe ran 2026-05-23 against API 2026-01 (the CLI pin) — confirms Item.description is OBJECT-typed and NOT SDK-gated. Same SDK-drift class as `hierarchy_type` / `views` (`@mondaydotcomorg/api` 14.0.0 doesn't expose `ItemDescription`), so the v0.9-M52-graduated two-layer wire selection-pin guard applies: cassette `match_query: /description \{/` (with brace — bare `description` rejects without selection-set) plus RUN_LIVE_TESTS `toHaveProperty('description')` against the production document. Decisions closed at pre-flight kickoff: D1 — Shape: narrow verb (vs extending ITEM_FIELDS_FRAGMENT universally vs both). User AskUserQuestion 2026-05-23 single round-trip in favour of the narrow verb per workflow.md "Read-side field-add" — heavy/nested fields carve out; lightweight/universal extend. Item.description is heavy (LIST<DocumentBlock> with JSON-scalar `content` payloads) and most items have no description; universal projection would bloat `item list` N-row responses without commensurate value. D3 — Wire-null normalisation: literal `description: null` on items with no description set (probed) normalises to the sentinel `{id: null, blocks: []}` at the parseItemDescription boundary. Keeps the single-resource envelope's `data` slot an iterable object so the table renderer's Object.keys iteration doesn't crash; agents distinguish absent (`data.id === null`) from present (`data.id !== null`). D4 — DocumentBlock projection: narrow 4-field (`id` / `type` / `content` / `position`) out of 9 wire fields. Dropped the 5 metadata-side fields (parent_block_id / doc_id / created_at / created_by / updated_at) — `doc get`'s DocumentBlock projection covers those for agents needing doc-block lineage. D5 — ERROR_CODES delta: zero. Registry stays at 29; new verb routes through existing not_found / usage_error / unauthorized / internal_error (with details.issues + details.reason discriminator for `malformed_item_row` shape regressions). Codex pre-flight review R1 CONVERGED (0 P1; 1 P2 + 1 P3 both applied inline at this commit). P2-1 added a `!isPlainObject(first)` guard before plucking `.description` so a malformed Monday row (e.g., `items: [42]`) surfaces as `internal_error` with `details.reason: 'malformed_item_row'` instead of collapsing to the "no description" sentinel. P3-1 tightened the unit-test selection-pin regex to scope the 4-field assertion INSIDE the `blocks { ... }` selection — a refactor dropping any of the named subfields now fails before reaching CI. Net stats: +1 command (118 → 119); 29 ERROR_CODES unchanged; 0 fixture ripples; 0 existing call sites touched; 4270 → 4293 tests pass (+23: 13 unit + 7 integration + 1 malformed-row guard + 1 new RUN_LIVE_TESTS-gated skip + the existing snapshot delta); branches 95.84% (≥ 95.45 floor); npm audit 0 vulns. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5d5eca1 commit f08bfef

11 files changed

Lines changed: 1585 additions & 20 deletions

File tree

CLAUDE.md

Lines changed: 81 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -188,18 +188,60 @@ humans are second-class. Built incrementally via Claude Code on top of
188188
HEAVY single-sourced one (`boardMetadataSchema` + `views`).
189189
Both correct per the runtime read; rule documents both valid
190190
choices.
191-
- **Next session:** **v0.11-G pre-flight contract diff.** v0.11
192-
candidate-selection ran 2026-05-22 per R-NEW-75 over the
193-
C/D/E/G + R-v0.10-NEW-4 backlog. SDK probe:
194-
`@mondaydotcomorg/api@14.0.0`**5th-consecutive stall**
195-
(v0.6 → v0.10), so M39/M40/M41 (SDK 15.x → API `2026-04`) +
196-
M44/M45 (SDK 16.x → API `2026-07`) stay DEFERRED. User picked
197-
**G — `Item.description` read-side, ships standalone** (the
198-
2026-05-22-at-M52-close pair-with-M40 user binding BROKEN at
199-
this candidate-selection per the 2-question round-trip);
191+
- **✅ v0.11-M54-G pre-flight CLOSED 2026-05-23**
192+
(`<pre-flight-sha>` — single commit: new verb `monday item
193+
get-description <iid>` + schema + tests + cli-design + output-
194+
shapes + plan-doc open). Picked at v0.11 candidate-selection
195+
2026-05-22 per R-NEW-75 over the C/D/E/G + R-v0.10-NEW-4
196+
backlog. **Empirical probe ran 2026-05-23** at pre-flight
197+
kickoff (`scripts/probe/v0.11-item-description-2026-01.report.txt`)
198+
— confirms `Item.description` is OBJECT `ItemDescription{id,
199+
blocks[DocumentBlock]}` at API `2026-01` (the CLI pin); NOT
200+
SDK-gated; ships standalone. **D1 closed via AskUserQuestion
201+
2026-05-23 single round-trip** in favour of shape (b) narrow
202+
verb (mirrors v0.9-M52's `board views` carve-out per the
203+
graduated "Read-side field-add" rule — heavy/nested fields
204+
carve out, lightweight/universal extend). **D2 selection-pin
205+
guard both layers landed** per v0.9-M52-graduated rule
206+
(cassette `match_query: /description \{/` + RUN_LIVE_TESTS
207+
`toHaveProperty('description')`; 3rd consumer of R-v0.9-NEW-6
208+
graduated guard). Per R-v0.9-NEW-2 the pure-additive new
209+
verb's wire leg IS the runtime payload at pre-flight — no
210+
stub literal, no PIN test, no RESERVED-literal guard (3rd
211+
structurally distinct instance: M50 deletion + M53 pure refactor
212+
+ M54-G pure additive — rule body widens at IMPL close, filed
213+
R-v0.11-NEW-2). 0 fixture ripples, 0 existing call sites
214+
touched. **R-v0.9-NEW-8 RESOLVED** at pre-flight (the canonical
215+
filing's open empirical probe + binding decisions both closed
216+
inline). **R-v0.9-NEW-10** advanced to 2-consumer (jsonScalarOrNull
217+
pattern re-used in `item-description.ts`; lift at 3rd consumer
218+
per R-NEW-58). 4 new R-class watch-items filed
219+
(R-v0.11-NEW-1/2/3/4 in `docs/v0.11-plan.md` §22 — multi_level
220+
probe gap, R-v0.9-NEW-2 3rd-class rule-widening candidate,
221+
`runByIdLookup` pluck-sub-field abstraction watch, `_lib.ts`
222+
DX-improvement). v0.10.0 stays PUBLISHED + release-complete
223+
(npm `latest` 2026-05-22T21:24:43Z; tag `v0.10.0` at `c9eceba`).
200224
**E (profile-scoped argument defaults) queued for v0.12**; C
201225
stays-filed (4 slips on Monday's `ColumnMappingInput` no-value-
202226
slot at API `2026-01`); D stays design-blocked.
227+
- **Next session:** **v0.11-M54-G IMPL close-docs + Codex IMPL
228+
review.** Per R-v0.9-NEW-2 the IMPL is unusually thin — no
229+
stubs to flip (the verb shipped LIVE at pre-flight). IMPL
230+
session work: (a) Codex IMPL review on the pre-flight diff
231+
(template at `.claude/templates/codex-pre-flight-review.md`
232+
applies; MEDIUM sensitivity; 0 P1 target); (b) §3 post-mortem
233+
+ §22 R-class log updates in `docs/v0.11-plan.md`; (c)
234+
`<pre-flight-sha>` backfill across plan-doc + CLAUDE.md; (d)
235+
fold R-v0.11-NEW-2 rule-widening into
236+
`.claude/rules/workflow.md` (R-v0.9-NEW-2 framing widens from
237+
"rejection-lift OR pure-refactor" to "any pre-flight with no
238+
deferred wire leg"); (e) cli-design SHA backfills if needed.
239+
After IMPL close-docs: **v0.11 release-prep** (version bump
240+
0.10.0 → 0.11.0 + CHANGELOG + README Scope flip per
241+
R-v0.9-NEW-15 widened checklist + close-docs sweep per
242+
R-NEW-82/84 baseline). E (profile-scoped argument defaults)
243+
opens for v0.12 candidate-selection after v0.11 ships.
244+
- **Historical context for v0.10 (previously the active milestone):**
203245
**R-v0.10-NEW-4 RESOLVED inline at `085e999`**
204246
`--maxWorkers=2` folded into the `test:coverage` script
205247
(work-of-opportunity per the v0.10 post-publish refactor-audit
@@ -584,7 +626,27 @@ detail, and R-class refactor backlog, **read the plan docs** —
584626
1. **[`docs/cli-design.md`](./docs/cli-design.md)** — canonical
585627
contract: command surface, output envelope, 29 stable error codes,
586628
deferral list (§13), every binding decision.
587-
2. **[`docs/v0.10-plan.md`](./docs/v0.10-plan.md)****ACTIVE plan**
629+
2. **[`docs/v0.11-plan.md`](./docs/v0.11-plan.md)****ACTIVE plan**
630+
(kickoff 2026-05-23). v0.11 = the **`Item.description`
631+
read-side carve-out** (`monday item get-description <iid>`) on
632+
the `2026-01` pin: **M54-G** ships a new narrow verb mirroring
633+
v0.9-M52's `board views <bid>` carve-out from `board describe`.
634+
Closes R-v0.9-NEW-8 (filed at v0.9-M52 close-docs;
635+
user-directed). **M54-G pre-flight CLOSED 2026-05-23**
636+
(`<pre-flight-sha>` — verb fully wired LIVE per R-v0.9-NEW-2:
637+
pure-additive new verb has no deferred wire leg → no stub
638+
needed). §22 R-class register populated at M54-G pre-flight
639+
(R-v0.11-NEW-1/2/3/4 + carried-forward v0.8 / v0.9 / v0.10
640+
open watch-items + promoted R-v0.9-NEW-8 → M54-G RESOLVED).
641+
**Next is M54-G IMPL close-docs + Codex IMPL review** (per
642+
R-v0.9-NEW-2 the IMPL is unusually thin — no stubs to flip;
643+
work is Codex review + post-mortem + R-class log + SHA
644+
backfills + R-v0.11-NEW-2 rule-widening fold into
645+
workflow.md) + release-prep (0.10.0 → 0.11.0 + CHANGELOG +
646+
close-docs, per R-NEW-82/84 baseline). M39/M40/M41 (SDK 15.x)
647+
+ M44/M45 (SDK 16.x) stay DEFERRED — SDK still 14.0.0
648+
(**5th-consecutive stall**).
649+
3. **[`docs/v0.10-plan.md`](./docs/v0.10-plan.md)** — shipped
588650
(kickoff 2026-05-22). v0.10 = the **`NOUN_DESCRIPTIONS` single-
589651
source-of-truth lift** on the `2026-01` pin: **M53** collapses
590652
~100 duplicate `ensureSubcommand(program, '<noun>', '<desc>')`
@@ -605,7 +667,7 @@ detail, and R-class refactor backlog, **read the plan docs** —
605667
CHANGELOG + close-docs, per R-NEW-82/84 baseline).
606668
M39/M40/M41 (SDK 15.x) + M44/M45 (SDK 16.x) stay DEFERRED —
607669
SDK still 14.0.0 (**4th-consecutive stall**).
608-
3. **[`docs/v0.9-plan.md`](./docs/v0.9-plan.md)** — shipped (npm
670+
4. **[`docs/v0.9-plan.md`](./docs/v0.9-plan.md)** — shipped (npm
609671
`latest` 2026-05-22T16:38:40Z; tag `v0.9.0` at `ee96681`). v0.9
610672
= the **multi-level board cluster** on the `2026-01` pin: **M50**
611673
multi-level subitem nesting (closes the M28 deferral + fixes the
@@ -645,14 +707,14 @@ detail, and R-class refactor backlog, **read the plan docs** —
645707
fixture-leftover-detection at close-docs after the M3 e2e M51
646708
leftover catch, MEDIUM; R-v0.9-NEW-12 Codex pre-flight
647709
findings-first behavior under `-xhigh` reasoning, LOW-MEDIUM).
648-
4. **[`docs/v0.8-plan.md`](./docs/v0.8-plan.md)** — shipped M49 (P1
710+
5. **[`docs/v0.8-plan.md`](./docs/v0.8-plan.md)** — shipped M49 (P1
649711
file-upload wire fix) + M46 (multi-file `--set`) + M47 (stdin
650712
`--set`) + M48 (board_relation/dependency settings) + the refactor
651713
cluster; re-scoped off the original 2026-07 SKELETON (M44/M45)
652714
per the v0.7-pivot precedent. §22 R-class log (R-v0.8-NEW-*) —
653715
**R-v0.8-NEW-22 PROMOTED to v0.10-M53 at this session**
654716
(status flipped OPEN → PROMOTED 2026-05-22).
655-
5. **[`docs/v0.7-plan.md`](./docs/v0.7-plan.md)** — shipped M42 +
717+
6. **[`docs/v0.7-plan.md`](./docs/v0.7-plan.md)** — shipped M42 +
656718
M43 (the v0.6.x bulk + create file `--set` carve-out folds);
657719
M39 (API `2026-04` pin bump) + M40 (`item set-description`) +
658720
M41 (`doc block-create-bulk`) **DEFERRED 2026-05-20** pending
@@ -664,18 +726,18 @@ detail, and R-class refactor backlog, **read the plan docs** —
664726
`.claude/rules/workflow.md` at M42 IMPL R7 + refined at R8;
665727
R-NEW-76 graduated from stub-anchored to wire-dispatch-anchored
666728
invariant at M43 IMPL).
667-
6. **[`docs/v0.6-plan.md`](./docs/v0.6-plan.md)** — shipped M38
729+
7. **[`docs/v0.6-plan.md`](./docs/v0.6-plan.md)** — shipped M38
668730
(files-shaped friendly `--set`) with §22 R-class log
669731
(R-v0.6-NEW-*).
670-
7. **[`docs/v0.5-plan.md`](./docs/v0.5-plan.md)** — shipped M34–M37
732+
8. **[`docs/v0.5-plan.md`](./docs/v0.5-plan.md)** — shipped M34–M37
671733
with §22 R-class log (R-v0.5-NEW-*).
672-
8. **[`docs/v0.4-plan.md`](./docs/v0.4-plan.md)** — shipped M29–M33
734+
9. **[`docs/v0.4-plan.md`](./docs/v0.4-plan.md)** — shipped M29–M33
673735
with §22 R-class log (R-NEW-72 through R-NEW-84 graduated).
674-
9. **[`docs/v0.3-plan.md`](./docs/v0.3-plan.md)** — shipped M19–M28
736+
10. **[`docs/v0.3-plan.md`](./docs/v0.3-plan.md)** — shipped M19–M28
675737
with §22 R-class log (R-NEW-1 through R-NEW-43).
676-
10. **[`docs/v0.2-plan.md`](./docs/v0.2-plan.md)** — shipped M8–M18
738+
11. **[`docs/v0.2-plan.md`](./docs/v0.2-plan.md)** — shipped M8–M18
677739
(R20–R53).
678-
11. **[`docs/v0.1-plan.md`](./docs/v0.1-plan.md)** — shipped M0–M7
740+
12. **[`docs/v0.1-plan.md`](./docs/v0.1-plan.md)** — shipped M0–M7
679741
foundations.
680742

681743
Supplementary: [`docs/output-shapes.md`](./docs/output-shapes.md)

docs/cli-design.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,6 +1797,33 @@ monday board group-delete <bid> <gid> --yes [--dry-run] v0.
17971797
# See §5.3 for board_id resolution and §5.5 for --where filter rules.
17981798
monday item list --board <bid> [--group <gid>] [--where <expr>]... [--filter-json <json>] [--state active|archived|all] [--all] [--limit <N>] v0.1
17991799
monday item get <iid> # single item with column values v0.1
1800+
monday item get-description <iid> # description (doc-block list) v0.11
1801+
# narrow companion read. Mirrors
1802+
# `board views` carve-out from
1803+
# `board describe`: the heavy/
1804+
# nested doc-block tree stays
1805+
# opt-in rather than bloating
1806+
# every universal item read.
1807+
# Surfaces ItemDescription
1808+
# `{id, blocks[]}` with each
1809+
# block carrying 4 of
1810+
# DocumentBlock's 9 wire fields
1811+
# (id, type, content, position).
1812+
# Wire-null description (item
1813+
# never had one) normalises to
1814+
# `{id: null, blocks: []}` so
1815+
# the single-resource envelope's
1816+
# `data` slot stays an iterable
1817+
# object. Raw GraphQL — the
1818+
# ItemDescription type is SDK
1819+
# 14.0.0-untyped, the `is_leaf` /
1820+
# `hierarchy_type` / `views`
1821+
# SDK-drift class. Paired-but-
1822+
# independent of v0.7-M40
1823+
# `item set-description`
1824+
# (write-side, SDK-15.x-gated);
1825+
# the read-side ships standalone
1826+
# at API 2026-01.
18001827
monday item find <name> --board <bid> [--first] v0.1
18011828
monday item search [--board <bid>] [--workspace <wid>] [--favorites] [--max-boards <n>] --where <col>=<val>... v0.1 (--board, --where); v0.3 (cross-board)
18021829
# single-board (v0.1, --board <bid>):

docs/output-shapes.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1809,6 +1809,46 @@ inline `title` per cell:
18091809
}
18101810
```
18111811

1812+
### `item get-description <id>` (v0.11-M54-G)
1813+
1814+
Single resource. Returns `Item.description` — Monday's
1815+
`ItemDescription { id, blocks[] }` shape with each block carrying 4
1816+
of `DocumentBlock`'s 9 wire fields (`id`, `type`, `content`,
1817+
`position`). Wire-null `description` (item never had one)
1818+
normalises to the sentinel `{id: null, blocks: []}` so the
1819+
envelope's `data` slot stays an iterable object — agents
1820+
distinguish "absent" via `data.id === null` from "present" via
1821+
`data.id !== null` (`data.blocks` may still be `[]` if every block
1822+
was deleted).
1823+
1824+
Raw GraphQL — `ItemDescription` is not exposed by
1825+
`@mondaydotcomorg/api` 14.0.0's typed surface (same SDK-drift class
1826+
as `Board.hierarchy_type` / `Board.views`); the two-layer
1827+
selection-pin guard (cassette `match_query: /description \{/` +
1828+
`RUN_LIVE_TESTS` `toHaveProperty('description')`) applies per
1829+
`.claude/rules/testing.md` "Wire selection-pin for raw-GraphQL
1830+
SDK-drift fields".
1831+
1832+
```json
1833+
{
1834+
"id": "8781640",
1835+
"blocks": [
1836+
{ "id": "b1", "type": "normal text",
1837+
"content": { "deltaFormat": [{ "insert": "Refactor login flow" }] },
1838+
"position": 1024 },
1839+
{ "id": "b2", "type": "bulleted list",
1840+
"content": { "deltaFormat": [{ "insert": "extract auth helper" }] },
1841+
"position": 2048 }
1842+
]
1843+
}
1844+
```
1845+
1846+
Wire-null case (no description set on the item):
1847+
1848+
```json
1849+
{ "id": null, "blocks": [] }
1850+
```
1851+
18121852
### `item find <name> --board <bid>`
18131853

18141854
Single resource on unique match. NFC + case-fold matching like

0 commit comments

Comments
 (0)