Tickets of other projects that block a dated epic, or that it depends
on, now show under «De otros proyectos» with a dashed bar, their arrows,
and the same warning when the dates clash («EPI-1 empieza el 25 oct,
pero BACK-1 (otro proyecto), que la bloquea, acaba el 5 nov»). Both
halves of a relation are read, since one recorded from the other project
only leaves its inward half here. The ones without dates, or out of
reach, are named under the chart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The project dashboard opens with «Salud del proyecto»: good, attention or
at risk, the worst of its signals, each with the cause and the tickets
behind it: stuck work, overdue and blocked tickets, critical or several
high bugs, QA sending back 30 % or more (with enough verdicts), releases
late or at risk by the forecast, columns over their WIP limit. Worked
out in the web from the insights it already reads (projectHealth). The
portfolio shows each project's light too, with its causes on hover.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the releases page, «Página pública» (owners and admins) opens a
secret link to a page with the release's notes, readable without an
account, also for private projects: the notes written, or the draft
from its finished tickets while there are none (without repeating the
title the page already shows). The link can be copied, renewed (the old
one stops working) or withdrawn. Stored in Mongo (`release_note_shares`).
API: shareReleaseNotes / unshareReleaseNotes / releaseNotesShare,
publicReleaseNotes(token); GET/PUT/DELETE /api/releases/{id}/public-notes,
GET /api/shared/release-notes/{token}. Web: /notes/:token.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
«📊 Añadir una encuesta» adds 2 to 6 options to a new comment. Members,
viewers too, vote by clicking an option (one vote each, which they can
change or take back) and see each option's votes as a bar. Whoever
started it, or an admin, closes it with «Cerrar y decidir»: the most
voted wins (none on a tie) and a «Decidido: …» reply marked as a
decision shows above the ticket. Events of the comment (voted, closed).
API: addComment(…, poll), votePoll, closePoll, Comment.poll;
POST /api/comments/{id}/poll/vote and …/poll/close. MCP: add_comment
poll, vote_poll, close_poll.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
«Pedir revisión» on a ticket asks a member (with an optional note): they
are notified and see it under «Te piden revisión» in my work until they
answer «Todo bien» or «Pedir cambios» (with what to change) from the
ticket's Revisiones box; whoever asked hears the answer and can withdraw
an open request. Events of the ticket (requested, answered, cancelled),
so they show in its history and the activity feed. Viewers can be asked
and answer; not oneself, nor twice the same person with one pending.
API: requestReview / answerReview / cancelReview, Issue.reviews,
myIssues(reviewing); POST /api/issues/{issue}/reviews,
POST …/reviews/{id}/answer, DELETE …/reviews/{id}. MCP: request_review,
answer_review, my_issues reviewing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With a card focused (Tab reaches them): arrows move between cards, also
across swimlanes' columns; Shift+←/→ moves the card to the next column
with the same rules as dragging it (allowed transitions, WIP warning,
reason when the status asks for one) and keeps the focus on it; p, e
and a open its priority, estimate or assignee; Enter opens it. The «?»
help lists them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A home panel with the tickets picked for today (10 at most), crossed out
as they get done and counted («1 de 3 terminados»), my open tickets to
add with one click, and what was left unfinished on my last day to carry
over. «☀ Mi día» on a ticket adds it or takes it out. The day is the
person's own (YYYY-MM-DD from the browser); private, no history (Mongo
`my_days`).
API: myDay(day), addToMyDay, removeFromMyDay; GET /api/me/day,
PUT/DELETE /api/me/day/{issue}. MCP: my_day.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a ticket, «Recordármelo» offers in an hour, this evening, tomorrow or
next Monday at 9, in a week, or any date and time, with an optional note.
The ticket shows the waiting one («Te lo recordaré el mié 21 a las 9:00»)
to change or drop. Every minute a scheduler tells the ones whose time
came as a REMINDER notification (bell, push, e-mail), once, and forgets
those whose ticket went to the trash or out of reach. One per person and
ticket, private, no history (Mongo `issue_reminders`).
API: setReminder / clearReminder, Issue.myReminder, myReminders;
PUT/DELETE /api/issues/{issue}/reminder, GET /api/me/reminders. MCP:
remind_me.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
«Seguir todo» on a ticket with subtasks (and on each epic of the Epics
page) tells you of it and of every ticket hanging from it, at any depth,
also the ones added later: their moves, assignments, comments and plan
changes, as a follower of each, plus when a ticket is placed under it.
Guests of a private project are left out. Personal, no history (Mongo
`tree_follows`).
API: followTree / unfollowTree, Issue.followingTree, PUT/DELETE
/api/issues/{issue}/follow-tree. MCP: watch_issue with tree.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The list shows up to 200 tickets and «select all» picks them all, but
the API takes 100 per bulk change: above that the whole change failed
(«At most 100 tickets at once») and nothing changed. The gateway now
sends batches of 100 one after another and adds up the results, for the
list and the backlog alike.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A top-level comment and its replies can be resolved by whoever can
change the ticket (CommentThreadResolvedEvent), with an optional
conclusion added as a reply marked as a decision, so it shows above the
ticket. A settled conversation folds to one line (topic, who settled it
and the conclusion) and can be unfolded or reopened; the header counts
the open ones, and finishing the ticket with one open asks first.
«Ana está escribiendo…» shows in the conversation being answered, at
once: a `typing` mutation publishes to a Redis channel per ticket
(`typing` subscription), never stored. In replies, Enter sends and
Shift+Enter breaks the line, except inside lists and code.
API: resolveThread / PUT-DELETE /api/comments/{id}/resolution, Comment
.resolved/resolvedBy/resolvedAt, typing mutation and subscription. MCP:
resolve_thread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mongo leaves empty objects out, so a rule saved with no conditions came
back without them: the automations list failed («Cannot return null for
non-nullable field Automation.conditions») and the rules engine threw
on it, so those rules never ran. The repository now gives them an empty
object, as the views of «my work» already did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings shows a native colour picker (the palette as suggestions)
instead of the list of eleven names. The API takes a palette name, as
before, or any #rrggbb; picking a palette colour still saves its name,
so it keeps its hand-made dark version. A free colour gets one variant
per theme, computed with the `color` library (moved towards white or
black until it contrasts 3:1 with the surface) and applied with
light-dark(): a pale yellow darkens on white, a navy lightens on dark.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owners and admins turn on a secret URL per project (Settings →
Integrations), POST /api/webhooks/alerts/{project}/{token}, that Sentry,
Uptime Kuma, Prometheus Alertmanager, Grafana or any tool sending JSON
with a title can call. A new problem opens a bug as TaskStream (label
«alerta», chosen type and priority); while that bug is open, the same
fingerprint goes to it (a comment at most every 30 minutes) and
«resolved» is said on it; once it is closed, a new bug names the old
one. Alerts of a project are handled one at a time, so two at once open
one bug, and a bug not yet projected still counts as open. Unknown
project, alerts off or a bad token all answer 403.
TaskStream may now comment as the system actor, like it already opened
tickets for public proposals. Stored in Mongo (alert_integrations,
alert_traces), not in the event store.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A toast offers to undo for seven seconds (or Ctrl/⌘ Z outside a field)
by running the opposite command: the card back to its column or the
ticket to its status (the workflow may refuse it), the previous
assignee, or the ticket back from the trash. The undo itself only says
«Deshecho». On the board, from the card's assignee; on the ticket page,
status, assignee and trash, which no longer asks for confirmation.
The toast is the app's own (UndoToastComponent): pi-snack-bar cannot
tell its button from the timeout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Whoever can change the ticket flags a comment as a decision (new event
CommentDecisionMarkedEvent on the comment). The ticket page lists its
decisions above the description, with a link to the comment in the
conversation (`?comment=<id>` scrolls to it and points at it), and the
search palette finds them by their text across my projects.
API: markDecision / PUT-DELETE /api/comments/{id}/decision; Issue.decisions
and GET /api/issues/{issue}/decisions; decisions(projectId?, search) and
GET /api/projects/{project}/decisions, GET /api/me/decisions. MCP:
mark_decision, search_decisions, and get_issue includes them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its main column was a grid without columns, so it grew to the width of
its widest form field (650 px on a 390 px screen) and cut the text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each person pins up to 20 tickets from the ticket's page («Fijar»). They
show as a strip under the projects on the home page and in the search
palette before typing. Private and without history, like the views of
«my work» (Mongo `pinned_issues`); tickets in the trash or out of reach
are left out, and when the list is full the unreadable ones are dropped
first.
API: GET /api/me/pins, PUT/DELETE /api/issues/{issue}/pin; GraphQL
myPins, pinIssue, unpinIssue and Issue.pinned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The card's title is now the link (stretched over the card), and the three
chips carry an invisible native select each, so they work by keyboard,
on a phone and with a screen reader without opening the ticket. Dragging
still works from anywhere on the card.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Redis consumer used to leave a failed event pending and go on to the
next ones, retrying it only at the next start. With Mongo down for a
while, a later change of the same ticket was then projected first, and
the version check dropped the earlier one for good (a status change
could vanish from the read model), besides the read side lagging until
a restart.
Now a failed event is retried in place, waiting longer each time (up to
30 s), and the next one waits for it. Failures of the infrastructure
(Mongo, Redis, the network: isTransientError) wait as long as it takes,
so the read side catches up by itself; an event that keeps failing on
its own goes, after 5 attempts, to <stream>:dead with its error, so it
does not hold up the rest.
GET /api/health reports the way from write to read (events waiting in
the outbox, pending to be projected, set aside) and says `degraded`,
still with 200, when something is stuck.
Tried for real: with Mongo stopped, two status changes went in; the
first failed to project with ECONNREFUSED, and once Mongo was back the
ticket's history had both steps, in order, without restarting the API.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the project dashboard, a day with activity can be picked on the map:
a side panel lists that day's moves grouped by ticket (each key opens
it), with the time of each, and the project's own (sprints, releases,
members) apart.
The activity feed takes an `after` date (GraphQL, REST), so with
`before` it reads a window such as a day; the MCP's
get_project_activity takes a `day`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each number sat under its label, and in a narrow panel «Vueltos de QA»
takes two lines, so its number fell lower than the others. The numbers
go on top now (the markup stays label then value).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pi/ui's slide toggle hides its input with display: none and gives it
no name: neither the Tab key nor a screen reader could reach the
switches in settings (visibility, the public's proposals, the
definition of done, the link for clients) or the bell's (e-mail copy,
notices on this device). They are pi-checkbox now, each with a name
saying what it turns on: reachable by keyboard, toggled with the space
bar and announced.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Visibility, the public's proposals, the definition of done and the link
for clients each had a hand-made switch; they use pi-slide-toggle now,
like the bell's, with their state written beside it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pi/ui gives every button a fixed height, side padding and a minimum
width. Three of ours, laid out by their own component, took it:
- a notice in the bell's panel stayed 40 px tall, so a two-line one ran
over the next;
- a day number in the calendar became a 40 px pill that ran into the
sprint bands under it (today's, coloured, showed it);
- «Responder» under a comment became a grey pill sitting on its line.
They keep their own box now, and «Responder» is a text link.
The home page shows my projects first, right under the greeting: what
most visits come to open.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owners and admins turn it on in Configuración → General, pick what it
shows (tickets per status, the sprint under way, release forecasts,
open epics and activity per day) and can renew or drop the link. Whoever
has it sees /share/{token} without an account: only those blocks, no
people nor single tickets but the epics, no links into the app, read
again every minute. A renewed or dropped link says it no longer works.
REST (GET/PUT/DELETE /api/projects/{project}/share and the public
GET /api/shared/dashboards/{token}) and GraphQL (dashboardShare,
shareDashboard, unshareDashboard, sharedDashboard). The token is kept
in dashboard_shares and goes with the project.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
«Modo pantalla» puts the dashboard full screen, over the rest of the
app: one block at a time and larger (the figures and the flow first),
moving on every 15 seconds, with the project, the time and the live
data. ← and → move, the space bar pauses and Esc leaves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three more blocks on the project dashboard, also by REST, GraphQL and
MCP:
- Activity: what moved each day of the last 12 weeks, in a heatmap,
with the busiest weekday and the longest lull.
- A ticket's way: for the tickets done in the last 90 days, where the
time from created to done went, status by status (waiting included),
and how much of it was work (flow efficiency).
- The public's proposals: received, waiting, under way, done and
discarded, and the most voted still unanswered.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
They come with @defer (on idle; on interaction): their code, their
stores and the bell's queries and live subscription leave the start,
and main no longer carries anything of the tracker's presenters or
application. Meanwhile the header shows a still copy of each with the
same box, so nothing moves when they arrive.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only what the header needs from the start stays at the root: the
session, my projects for the command palette, the inbox and push for
the bell, and their adapters. The home page's facades (my work, the
rest of the home, my views, the calendar), Explore's and the proposal
form's are provided by their routes, whose injectors keep them for
when you come back. The other adapters come with the tracker's routes,
now loaded lazily.
The web libs declare "sideEffects": false, so a chunk carries what it
uses and not everything an index.ts exports: main goes from 1.31 MB
to 432 kB of the same initial total, which is still held up by @pi/ui
(one module, with all of Material) and the GraphQL documents, both
needed by the header.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/cartera, /explorar and /p/:key/proponer are /portfolio, /explore and
/p/:key/propose now, like every other path; the Spanish ones redirect,
so links already shared keep working. Settings sections go by
?section= with English names.
The portfolio reads itself again, quietly, when a ticket, sprint or
release of the projects shown changes, when my projects change, or when
the live connection comes back; «En directo» replaces the refresh
button.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The home page was two grids of panels side by side, each row as tall as
its tallest panel, which left gaps. Now: four figures of the same size
(my open tickets, waiting for QA, finished by me and by the team this
week), the week, my views, and two columns that stack on their own: my
tickets and the latest on the left; sprints, QA, following, releases,
who is online and «Retomar» on the right. Project cards are as tall as
their row, with the team at the bottom, and a phone only lists the
days of the week that have something.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The settings page was one long strip of panels of uneven widths. It has
a menu now (General, Flujo de trabajo, Tickets, Equipos,
Automatizaciones, Integraciones, Importar y exportar, Zona peligrosa),
one section at a time and in the URL (?seccion=), each panel full width.
A dot marks the sections with unsaved changes, and the save bar follows
whichever section is open. On a phone the menu is a row of chips.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A type row needed some 41rem in a panel of about 26rem, so names were
cut short and the selects spilled out. It takes two lines now (three
on a phone), and on a phone an area keeps «Archivar» beside its name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A field's label floats above its box and only other fields leave room
for it, so «Título» sat on the type selector. Also: the mention hint
had the shell's paragraph size instead of a hint's, and «Asignar a» and
«Release» offered «Todas las personas» / «Todas las releases», filter
words, for «nobody» and «none».
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening a drawer form refilled its model only, so a field touched the
time before came back showing its errors. They reset the form state
now (touched and dirty) along with the values.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The image installs only the dependencies listed in apps/api/package.json,
and web-push was added to the root one alone with push notifications, so
the API died at start with «Cannot find module 'web-push'» and its
healthcheck never passed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Greeting and summary of the day, the coming week, the latest across my
projects, who is online now, the week in numbers (mine and the team's,
with a streak), sprints under way, «Retomar», project cards with their
activity, what to pick up when nothing is mine and first steps for
newcomers. REST (GET /api/me/home), GraphQL (myHome) and MCP
(get_my_week).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/cartera (and GraphQL portfolio, REST /api/me/portfolio, MCP
get_portfolio) shows every active project I belong to (not archived, not
as a guest) at a glance: open, overdue, blocked and stuck tickets, the
sprint under way and whether it gets there, planned releases with their
forecast, and the open epics with their progress. It reuses the
dashboard's computations. A release with no tickets now reads «Sin
tickets» instead of «Todo hecho», here and on the dashboard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A project member can be a GUEST. In a private project a guest only sees
the tickets they take part in (opened, assigned or followed): every other
ticket answers 404, also in my work, realtime changes, blockers and
linked tickets, and project-wide views (activity, statistics, dashboard,
releases, sprints, epics, trash) answer 403. Guests open tickets without
assigning or planning them, and comment, attach, follow and vote on their
own; they change nothing else. In a public project they see what anyone
does. The web gives them only Tablero, Lista and Miembros and loads none
of the project-wide data.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A ticket's page beats every 10 s (touchPresence / PUT
/issues/{issue}/presence, with whether I am editing) and gets back who
else is there, editing ones first. Entries live in Redis, one hash per
ticket, and fade after 30 s without a beat; leaving the page takes me out
at once. Only for people who can read the ticket; never in the event
store. The ticket shows «Bob está editando · Ana también lo está viendo»
above its title; editing means a ticket or comment form is open or a
comment is being written.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Projects keep teams of members in their settings (settings.teams; people
outside the project are dropped, teams are archived, not removed; absent
when saving means unchanged). Tickets go to a team (`team`, also filtered
in the list and in MCP search_issues), and a comment mentioning
@[Frontend](team-FRONT) tells each of its members. Settings has the teams
with their members, the ticket a «Equipo» select, the list a team filter,
and «@» offers the teams after the people.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner archives a finished project (archiveProject, POST
/projects/{project}/archive) and brings it back (unarchive). While
archived it stays readable, managers still read its rules and webhooks,
but no command changes it: contributing and managing checks refuse with
422, votes and public proposals too, and automations do not run. The web
shows a read-only notice on every page of it (with «Desarchivar» for the
owner), hides what edits, marks it on the home page and lists it last.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A project can ask that tickets reach a DONE status only with their
checklist ticked (settings.checklistBeforeDone, off by default; absent
when saving means unchanged). Members get a 422 telling how many steps are
left; owners and admins may still close it. Settings has the switch and
the ticket's checklist says when the project asks for it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A person saves named views of «my work»: projects, assigned to me, that I
follow, status categories, priorities, bugs only, overdue and text. They
are private (Mongo my_views, no history), listed and deleted by their
owner (GraphQL myViews / saveMyView / deleteMyView, REST /api/me/views),
and run as the cross-project search, which gains project, priority,
bugsOnly and overdue filters (also in MCP my_issues). The home page shows
them as a bar above «my work», with a drawer to save a new one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Services run commands through a CommandRunner that runs one again (up to
5 times, waiting a little longer each time) when its aggregate changed
underneath, e.g. a saga writing to the same ticket. Each command loads its
aggregate fresh and stores its events in one transaction, so a
ConcurrencyError means nothing was stored and running it again is safe.
People no longer get a 409 for it, and the e2e suite stops flaking.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The insights reading adds quality (open bugs per priority, bugs created
and closed per week for 8 weeks, QA returns and passes per area over 90
days, bugs reported in the 14 days after each of the last three
releases) and, for each list custom field, open tickets per option. The
dashboard shows both; the field to split by can be picked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The insights reading grows (GraphQL insights(since:), REST ?since=, MCP):
- planned releases with a likely (p50) and safe (p85) finish date from a
Monte Carlo over the last six weeks of finished work, one after another,
and a verdict against their target;
- with `since` (the viewer's last visit, kept in the browser): tickets
created, finished and sent back from QA, and their mentions;
- each person's open points against what they closed per sprint lately;
- open tickets without estimate, area or assignee, or untouched for 90
days, each count opening the list filtered (new noEstimate, noArea and
updatedBefore filters).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new projectInsights reading (GraphQL Project.insights, REST
/projects/{project}/insights, MCP get_project_insights), computed by pure
functions over the read model and the timeline:
- the sprint under way: days left, points done, pace against what is
needed and whether it gets there;
- tickets at risk: overdue, due within a week, blocked by an open ticket
and stuck (7 days or more in an in-progress or QA status);
- aging of the work under way against the p85 cycle time;
- average days in each in-progress and QA status, with WIP limits.
The dashboard shows them in four new panels, reloaded on every change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>