Releases: aenix-io/aeman
Release list
v0.33.1
A cancelled review leaves the board
Seen on the live board: a card titled review: … standing in the Triage strip as unscheduled work, on nobody's own board, and nothing to say what it was for.
Cancelling a review used to hand the card back to the closing sprint when it sat in the current one — the thought being that it should not linger on the new sprint. What that left was a card with the review link broken, no progress and no week, which is exactly the shape the strip holds up as work nobody has scheduled. So it asked to be planned, under the title of a review that had been called off, while being invisible on every personal board because its dates were yesterday's.
A cancelled review is now off the board wherever its sprint had put it — which is already what happened to one outside the current sprint, so this removes a special case rather than adding one.
Unchanged, and the reason the rule stays narrow:
- a review the reviewer has already worked on (progress above 0) is never touched — that work is theirs;
- a finished review card stays as the record that the review happened.
Nothing is lost either way: the day the card stood on gives it back, and the cancellation is recorded on the original, which is where a person looks for what became of the review.
v0.32.1
The tail of the review that went into v0.32.0.
Fixed
- The refuse stage was offered where it cannot be given. The Me board draws more than the cards on you — a parent joins it when any of its subtasks is yours — and the stage was offered on all of them, so the menu opened on a card nobody had given you and the click came back an error. Refusing is a first-person act, and the board now offers it only where it can be said.
- The refused bar is held to a contrast floor against the track it sits in: it is the darkest bar on the board by design, but dark must not mean invisible.
- The fourth hand-written copy of "is this card done" asks the shared rule instead.
Tested
web/src/stages.ts — the copy the board actually draws with — had no test at all, while the Go side it mirrors was covered thoroughly. It has thirteen cases now, both edges of the 10–90 band included.
One gap a TypeScript test cannot close is closed from Go: a stage TypeScript has never heard of is invisible to a test that iterates TypeScript's own list. A new assertion reads the mirror and requires it to name every stage the domain has — a stage added to one and not the other reaches the browser as no stage at all, which is how refuse first shipped, drawn as ordinary work in progress with a green bar and no clamp.
Documented
The sprint's spec.capacity; the front-matter key order, which omitted leftAt; and the MCP surface, which has no place_card tool — an agent scheduling work for a later week must clear the dates itself, or the card stands on today's board and in a future week at once.
v0.32.0 — Triage
Triage — a fourth board, and the weekly plan it replaces
The board had a day (Me, Team) and a plan by columns (Project), and nothing between them: work that was accepted but not for now went into today's Unplanned and was done next, whatever else was planned. Triage is the weeks laid side by side — people across, weeks down — so that putting something in ahead of something else means moving that something else.
- A week is stacked in the order somebody triaging wants to meet it: overdue first, then the project's work, then the processes', then the zones.
- Weeks work still owes are drawn before they exist — a process's coming turns, a recurrent card's next rounds — as outlines nothing can be done to.
- A team's capacity is the roster's number, or what the team has actually been finishing over the four complete weeks before this one.
The weekly plan is gone
Its panel, the wed/fri bands and the actions that moved cards between them are removed. What the panel held is now the grid's: a card placed in a week stands there all week, in its person's column or in Unassigned when nobody has taken it.
The × says which of two things it is doing
It used to decide for itself, so taking a card off the board needed the gesture twice. Now the request carries the answer — unassign leaves the card in the week or column that still holds it, off the board takes it away — and the board asks before it acts. Each is taken at its word: neither a Project-board slot nor a process turn is this board's to destroy.
A person answers for their own board
Work is added for oneself only as unplanned; the × reaches only what the person put there themselves; a drag reorders inside a zone and never between them. Instead of removing what they will not do, a person has a new stage: refuse — their answer and nobody else's, which leaves the card standing on the team's grid for the lead to answer.
Breaking changes
spec.plan {band, week}becomes a flatspec.week, which must be a Monday.take-into-plan/release-from-plan,view=weeklyand the?week=selector are gone.POST …/actions/removelosesfromand gainsintent.boardservice.Service.Removetakes aRemoveIntent. It is a string type, so an old call passing an untyped constant still compiles and silently degrades to the intentless gesture — passUnassign,OffBoard, or"".- A stage name the board does not have is now refused rather than stored.
v0.16.0
Listings are board rows; the body is the detail pane's
A board view used to deliver every card's full description — 76–84% of a day-view payload, unused by row rendering — and agents reading a board over MCP paid the same tax at board scale. Listings now come as the board row: title, team, zone, assignees, progress, stage, dates, plus the link refs extracted from the description server-side (status.links, capped at 50), so the row's links indicator works without the body. The description always was, and remains, resolved by its own endpoints: GET /cards/{uid} / get_card for the body, /links for resolved link titles, /log for the feed.
fields=full (REST) / full=true (MCP) opts genuine bulk readers into complete cards. This is a breaking change for API/MCP consumers that read descriptions from listings — pass the flag. The new title= substring filter on list_cards resolves a card someone mentioned by name to its uid in one cheap call.
Day views measured on the production board: me 103→33 KB, team 426→80 KB, view=all 2543→1124 KB.
Hardened by independent review
The branch went through two adversarial reviews before merge; everything they found is fixed in this release, most notably: editing a description is now gated on its body having actually loaded (previously a fast Save on a just-opened card could write "" over the real text), a full Card resource always carries spec.description — present even when empty — so "not loaded" is unambiguous, the detail dialog keeps a dirty draft safe from background refreshes, a failed body fetch retries on re-select, and a description edit refreshes the row's links icon immediately.
SPA tabs from older releases show empty description panes until reloaded — the deploy restart logs everyone out, so the reload happens on next sign-in.
v0.15.0
Copy a card's id straight from the board
Agents act on a card by its uid — every MCP card tool takes it — and until now the UI never showed it. Hovering a card offers an ID button that copies it to the clipboard, ready to paste into a chat with an agent.
It takes the slot of the hover pencil, which the card no longer needs: double-clicking a card opens the detail pane, and that already renames it and edits its description. The now-unreachable inline title editor went with it.
The progress handle is easier to grab
The handle is an 11px dot on a 3px line, and people kept missing it. Its press target now reaches well past the drawn dot — 11px to each side, the axis a pointer misses along, and down to the card's edge — without moving a pixel of what is drawn. At high percentages, where the handle slides under the row of buttons, those buttons now win the press, so aiming for one never starts a drag instead.
The bar itself stays inert on purpose: making the whole track draggable would help aim, but it spans the card, and a stray press while dragging cards around would silently rewrite someone's progress.
This image also carries v0.14.0 (the ID button), which was tagged but never published.
v0.14.0
Copy a card's id straight from the board
Agents act on a card by its uid — every MCP card tool takes it — and until now the UI never showed it. Hovering a card now offers an ID button that copies it to the clipboard, ready to paste into a chat with an agent.
It takes the slot of the hover pencil, which the card no longer needs: double-clicking a card opens the detail pane, and that already renames it and edits its description. The now-unreachable inline title editor was removed with it.
v0.13.0
Scheduling a card ahead now actually parks it
Two things went wrong when a new card was dated into the future.
It joined the current sprint. No sprint covers a future day — sprints are daily and open as they start — so the card sat in a sprint it would never be worked in: dragged forward by every daily carry-over, its dates reading "current sprint" the whole time, its log collecting a sprint event a day while nobody touched it. aeman already had the right state for this — the "next sprint" create leaves a card sprint-less and the carry-over that reaches its day adopts it — and a future day now takes that path, on create and on reschedule. An explicit sprint still wins.
Its day counter counted from creation, so a card made today for next month arrived three weeks old and coloured as rotting. The counter means days on the board, and a parked card is not on the board: it now counts from whichever came later, the card's creation or the day it landed. Ordinary cards are unaffected — a card open for ten days still reads 10d.
Note for teams that lean on the counter as a nagging signal: a card deferred with +1 day / +1 week also moves its start day, so its age now restarts when it comes back. The defer history stays in the card's log.
This supersedes the documentation-only answer given in #82 — the reports were right, and the behaviour is fixed rather than described.
v0.12.1
A bolded link is a link again
Card descriptions are markdown, and links in them are routinely written bolded — **https://…/pull/1360**. The trailing asterisks ended up inside the URL, so the reference stopped being recognised as a GitHub one: no title, no state, no dedupe against the same item written as an owner/repo#N shorthand — just a bare link in the card's menu, while an unadorned link on the same card resolved fine.
Markdown emphasis (*, _, ~, backticks) is now trimmed off a URL's tail alongside sentence punctuation, matching GFM's autolink rule, in both the server extractor and its client mirror. Punctuation inside a path is untouched. Existing cards fix themselves — links are parsed on read, so nothing needs migrating.
Clearer MCP docs for scheduling ahead
create_card / update_card now explain what a future start actually does: it parks the card off the board until that day. Sprints are daily and created as they start, so no sprint exists for a future day yet — the card legitimately keeps the team's current sprint in its dates until a carry-over adopts it on the day. The old wording invited agents to "correct" the sprint field by hand, which would only drag the card back onto today's board (reported in #82).
v0.12.0
First open is no longer a 20-30s progress bar
Two changes to the shared board cache, reviewed independently for concurrency, security and operational cost:
Probe-based cache admission. Proving that a signed-in user's token can read the board now costs one tiny GraphQL query instead of a full multi-page board load. Coming back from a break admits you into the warm shared cache in ~half a second; an aging access proof refreshes in the background while your read is served instantly. The probe is exactly as strict as the full load it stands in for — a parity test pins the two verdicts together across not-found, rate-limit, revoked-token, insufficient-scope and partial-error fixtures.
Off-request board warmer. One server-side loop per board keeps the cache fresh while anyone has the board open, and on a slower cadence for 16 hours past the last read — carrying it through the night so the morning's first open is served instantly. It replaces the per-tab background revalidation entirely, cutting steady-state GitHub traffic from roughly one full reload per active tab per minute to one per board per few minutes. The warmer rides the most recent user's token, stops when that user's session ends, tolerates transient GitHub hiccups, and never vouches for anyone's access.
Mutations benefit too: with the cache held inside the fresh window, card edits no longer block on a full board reload.
The only remaining cold load is the very first request after a server restart. External edits made directly on GitHub surface within a few minutes for passive viewers (active users see them as fast as before).
v0.11.2
Deferring a card clears it from the sprint in progress
Deferring an old card (+1 day / +1 week) left it on the Team grid in the very sprint it was pushed out of, whenever the team's current sprint had opened on an earlier day. The "keep it as history" exception now applies to closed sprints only — never to the team's current pointer — so a deferred card leaves the board at once and comes back on its own day.
A card created from a link reads properly right away
Pasting a GitHub link as a card title used to leave the raw URL on screen until a page reload: the create blocked on a GitHub round trip, and the optimistic card rendered the typed URL.
- The create no longer waits on GitHub: it answers under the readable
Pull: owner/repo#Nlabel, and a background pass renames the card to the item's real title. - A card retitled by a person or an agent meanwhile keeps those words — the background rename only replaces the untouched label.
- The rename is broadcast to every watcher, the creating tab included, so it lands without a reload.
- The client shows the same readable label in its optimistic card, so the raw URL never appears at all.