Skip to content

Agent ergonomics: shorthands, batch writes, schema refresh, and a drift-guarded skill doc - #1

Merged
Arnonrgo merged 6 commits into
masterfrom
feat/agent-ergonomics
Sep 8, 2026
Merged

Agent ergonomics: shorthands, batch writes, schema refresh, and a drift-guarded skill doc#1
Arnonrgo merged 6 commits into
masterfrom
feat/agent-ergonomics

Conversation

@Arnonrgo

@Arnonrgo Arnonrgo commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

mcli is driven by LLM agents in steady state, and this branch fixes the places where it made agents guess or gave them a misleading answer. Six commits, each building and testing green on its own.

What changes

fix(cli): malformed calls report USAGE, not INTERNAL. INTERNAL reads as "mcli broke, retry"; USAGE reads as "your call was wrong, fix it". Cobra's post-parse validation bypasses FlagErrorFunc entirely, so mcli item update <id> --status Done (no --board) exited 5 and invited an agent to retry a call that can never succeed. Also drops CodeConflict, which was specified but never emitted, and adds errs.AllCodes so docs can be tested against the real set.

feat(item): typed shorthands and stdin batch writes. --status Done addresses the board's single status column instead of requiring a column lookup plus hand-encoded monday JSON. Where a board has 0 or 2+ columns of a type, the command fails and names the candidates rather than picking one. Passing - reads up to 500 rows from stdin and writes them in one request against monday's per-minute complexity budget instead of N; every row is validated before the first request, and the response reports errors[].index so only failed rows are retried — item creation has no dedupe key, so re-sending the batch would duplicate.

feat(api): schema staleness reporting and refresh. A stale embedded schema silently misrepresents the API surface, so an operation that exists reads as missing. mcli api now warns on stderr (never stdout, which carries the JSON contract), and mcli schema status / refresh make the schema's age a fact rather than a guess.

feat(board): column descriptions are actually readable. --description wrote one, but the field was missing from every column selection — so board column list and board get dropped it, and column describe never echoed the text it had just set. Added to all five selections, surfaced in --pretty, and the two column tables now share one renderer instead of each carrying its own tabwriter (the duplicate boardColumn struct is gone — that duplication is what let the views drift).

docs: the skill doc is rewritten and guarded. Nothing tied it to the command tree, which is how it omitted board create --workspace — a flag whose absence returns API: User unauthorized to perform action, an error that says nothing about workspaces. It moves out of a Go string literal into skill.md (//go:embed): a Go raw string cannot contain a backtick, so the old + "\" +form made the doc unreviewable, which is precisely how it drifted.TestSkillDoc_MatchesCommandTreenow resolves 85 command paths and 79 flags against the real cobra tree, including inside fenced blocks, andTestSkillDoc_AuditCatchesDrift` proves that audit can still fail.

Verification

  • gofmt, go vet, golangci-lint run (0 issues), go test ./... — green
  • Each of the six commits verified independently in a scratch worktree: build OK, tests OK
  • Column descriptions verified live against a staging board: create with/without a description, describe, and both --pretty tables, including a non-ASCII description to exercise rune-safe truncation

🤖 Generated with Claude Code

Arnonrgo and others added 6 commits September 8, 2026 13:48
The workflow watched `main`, which this repo does not have, so push builds
never ran — only pull_request ones did.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mcli is driven by LLM agents in steady state, where INTERNAL reads as "mcli
broke, retry" while USAGE reads as "your call was wrong, fix it". Cobra's
validation failures arrived unclassified and exited 5, inviting agents to retry
calls that can never succeed.

Flag *parse* failures now route through a FlagErrorFunc, and the checks cobra
runs after parsing — which bypass that func entirely — are matched on their
message markers: argument counts, flag groups, and MarkFlagRequired violations.
The last is the most common malformed call mcli sees, since nearly every item
and column command requires --board.

Namespace commands also reject unknown subcommands instead of printing help and
exiting 0, so `mcli item lst` no longer looks like a success.

Adds errs.AllCodes so docs can be verified against the real set of codes, and
drops CodeConflict, which was specified but never emitted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Writing an item previously meant looking up a column ID and hand-encoding
monday's raw column JSON. Shorthands (--status, --date/--due, --number, --text,
--checkbox) address the board's single column of that type and are validated
before anything is sent. Where a board has 0 or 2+ columns of a type, the
command fails with USAGE and names the candidates rather than guessing.

Passing `-` in place of --name/<id> reads rows from stdin (JSON array or one
object per line, max 500) and writes them in one request against monday's
per-minute complexity budget instead of N. Every row is validated up front, so
a bad label fails the batch without a partial write, and the response reports
errors[].index so only the failed rows are retried — item creation has no
dedupe key, so re-sending the whole batch would duplicate.

--dry-run prints the exact column_values that would be sent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every 'mcli api' operation is generated from the embedded schema, so a stale
schema silently misrepresents the API surface — an operation that exists reads
as missing. 'mcli api' now warns on stderr (never stdout, which carries the JSON
contract) when the schema is old, and 'mcli schema status' / 'mcli schema
refresh' report which schema is in use and replace it.

The embed carries a fetched_at stamp so age is a fact rather than a guess, and
'config set api-version' reuses the shared fetch-and-cache path instead of
duplicating token resolution and file writing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A column description is the only place a board records what a column *means*,
which matters most to the next agent reading it. 'board column create
--description' wrote one, but no read path returned it: the field was missing
from every column selection, so 'board column list' and 'board get' dropped it
and 'column describe' never echoed the text it had just set.

Adds description to all five column selections (the two reads, plus the three
mutations so writes echo back), and surfaces it in --pretty. The two column
tables now share one renderer instead of each carrying its own tabwriter, and
the duplicate boardColumn struct — field-for-field identical to columnOutput —
is gone; that duplication is what let the two views drift. DESCRIPTION is the
last column so long prose cannot widen the columns to its left.

truncate now counts runes rather than bytes: it is fed user-authored
descriptions, where byte slicing would split a rune and emit U+FFFD.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The skill doc is mcli's primary contract for LLM callers, and nothing tied it to
the command tree — which is how it came to omit `board create --workspace`
entirely, a flag whose absence produces `API: User unauthorized to perform
action`, an error that says nothing about workspaces. Meanwhile the optional
Semantic Layer section spent 23% of the doc's budget.

The doc is now organised by what an agent actually does, in frequency order:
discover IDs, read, write, recover from an error. Board setup, previously first,
is the rarest operation. It gains a "Failures Worth Knowing About In Advance"
section for the cases the error message alone will not explain, and the error
table now says what to do next per code rather than just naming it.

It moves out of a Go string literal into skill.md, embedded with //go:embed.
Markdown is full of backticks and a Go raw string cannot contain one, so the old
`+ "`" +` form made the doc effectively unreviewable — which is precisely how it
drifted.

TestSkillDoc_MatchesCommandTree resolves every command path and flag the doc
mentions (85 and 79 respectively, including inside fenced blocks) against the
real cobra tree, and TestSkillDoc_AuditCatchesDrift proves that audit can still
fail. TestSkill_Concise now budgets bytes rather than lines, because bytes are
what the doc costs an agent's context: reflowing a paragraph changes the line
count without changing the cost, while one dense table row costs far more than
one short line.

Also: README and demo examples refreshed to real output, two new demos, ADR-002
records the never-emitted CONFLICT code's removal, and the stale plan docs are
deleted per the repo's convention that committed docs describe shipped reality
(git history preserves them).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Arnonrgo
Arnonrgo merged commit cc68942 into master Sep 8, 2026
1 check passed
@Arnonrgo
Arnonrgo deleted the feat/agent-ergonomics branch September 8, 2026 10:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant