@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>
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>
/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 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>
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>
Text, number, date or option-list fields defined in the project settings
(archived, never removed; their type is fixed once saved). Tickets hold
them, edited in the side panel, listed under the title and filtered by
option in the list view; also over GraphQL, REST (?field=ID:value) and MCP.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Postgres: every table has a uuid `id` (gen_random_uuid()); the event
store keeps its relay order in `seq`, and the lookup tables their
uniqueness in constraints of their own.
- MongoDB: `_id` is Mongo's ObjectId; `id` is the domain id (the same
uuid as the aggregate's stream in Postgres), unique, and every query
goes by it. Production starts from empty databases.
- Web: linking tickets and hanging one from a parent are picked from a
searchable select (links across projects, parents within the project)
instead of typing a key; every checkbox is @pi/ui's pi-checkbox.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Formatted text
- Descriptions, comments, replies and release notes render Markdown,
sanitised with DOMPurify (tested against scripts, handlers and
javascript: links). A pasted image is uploaded and written as
; the web fetches it with the reader's session,
so it works on private projects. Every text box has a preview. The
attachment upload now answers its attachmentId.
List view
- /p/:key/list: every ticket as rows, sortable by any column, filtered by
text, open/closed, type, priority and assignee; status, priority and
assignee change in place; bulk edit (the backlog's bar, now shared).
Ticket details
- Reactions on comments (GitHub's eight), for members and viewers.
- Duplicate a ticket: what it says, not where it stands.
- blockedBy: open tickets it waits for (BLOCKS / DEPENDS_ON), worked out
when reading with one query per board or page; shown on cards, list
rows and the ticket.
Public proposals
- A public project can take bug reports and ideas from anyone
(acceptsProposals): POST /api/explore/projects/{project}/proposals and
the form at /p/:key/proponer. They become tickets labelled «propuesta»,
opened by TaskStream; a honeypot field and 5 per address every 15
minutes keep spam out. No e-mail is asked: it would be public.
Votes
- Anyone signed in votes on a public project's tickets, members on a
private one (voteIssue, PUT/DELETE /api/issues/{issue}/vote): votes and
voted on each ticket, buttons on the ticket and the roadmap, a column
in the list. The showcase carries votes and the open proposals.
MCP: vote_issue, duplicate_issue, react_to_comment; listings show
blockers and votes. Docs, Word and PDF updated; chat notifications
(Discord, Slack, Telegram) noted for later.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Git, automations, webhooks and import cards had no inner padding and
oversized buttons, because the settings page's styles do not reach child
components; they now share a stylesheet with the same pills. The template
button gets its own column instead of squeezing the type's name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each ticket type can carry a starting description and a checklist, edited
in the project settings. New tickets of that type get the description
when they come without one and always get the checklist; the new ticket
form says what the template will add.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owners and admins can set rules of the kind «when a ticket is created,
moves, gets assigned, gets a comment or its pull request merges; if it is
of this type, priority, area or label; then move it, assign it, label it,
change its priority, comment or put it in the active sprint». Rules act as
TaskStream within the workflow, run once per event and trigger no others.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every ticket of a project can be downloaded as CSV, and a CSV exported
from TaskStream or from Jira can be imported: types, statuses, priorities
and people are matched by id or name, the rest falls back with a warning,
and parents are rebuilt. The settings page previews an import before it
creates anything.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owners and admins can point a project at other systems: a signed JSON
POST (HMAC-SHA256 of the body) when tickets, comments, releases or sprints
change, filtered by event or family. Deliveries are retried a couple of
times, the last one is shown, and a ping can be sent from the settings.
Private addresses are refused unless WEBHOOKS_ALLOW_PRIVATE is set.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A project can turn on a webhook for GitHub, Gitea or GitLab. Pushes and
pull or merge requests that name a ticket key show on that ticket under
Desarrollo, with the state of each pull request; the secret is shown and
rotated from the project settings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tickets get a start date next to their due date, both editable on the
ticket page. The Hoja de ruta tab lays the epics out over the months as
bars filled with their progress, with sprints and releases as landmarks
and a line for today.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each status can carry a WIP limit in the project settings; the board shows
it next to the count, marks a column that goes over it and says so when a
card is dropped there. The board can also be grouped in rows by person,
epic, priority or type, remembered per browser.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Public projects:
- Project.isPublic (create/update, owners and admins). Anyone can read a
public project without a token: project, board, tickets, comments,
history, releases, activity, tracking and statistics, over GraphQL and
REST. Writes stay for members (401 anonymous, 403 outsiders).
- @OptionalAuth() / @ApiOptionalPireonAuth(): anonymous callers allowed,
invalid tokens still rejected. ReadAccess lets anonymous viewers read
public projects only; linked tickets are filtered by visibility.
- publicProjects (GraphQL) and GET /api/explore/projects (REST).
- Web: «Explorar» page, project pages open without a session (read-only
notice, sign-in hints), public badge, visibility switch in settings,
no realtime subscriptions without a session.
Project deletion:
- Owner only, confirming the key (deleteProject / DELETE
/api/projects/{project}?confirm=KEY). The key is freed; the read model
drops the project and members are notified (web goes home).
Sagas (run by the EventsBridge after projections, durable and ordered):
- ProjectDeletionSaga: project deleted → delete its tickets and releases;
ticket deleted → purge its comments and drop links from other tickets.
- ReleaseArchivedSaga: unfinished tickets leave an archived release.
- UserDeactivatedSaga: the Pireon webhook records UserDeactivatedEvent in
the event store (EventRecorder); the tracker unassigns that user's open
tickets as the system actor.
- Internal idempotent commands; domain refusals are logged and skipped,
concurrency conflicts retried. Affected streams come from the event store
(TrackerStreamsRepository).
Tests: domain lifecycle specs, saga specs run like the bridge, facade
tests, e2e for public access, cascade deletion, archived releases and
deactivation webhook. Docs, READMEs, schema.gql/openapi.json and Word/PDF
updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Per-project settings (owners/admins, «Configuración» tab):
- Statuses with category (TODO, IN_PROGRESS, QA, DONE, CANCELLED), colour and
"requires reason"; configurable transitions; ticket types (icon, colour) and
areas. Defaults are the previous fixed values; projects created before keep
them without migration.
- Values are archived, never deleted: tickets, history and statistics keep
referring to them. Validated in the domain (ProjectSettingsChangedEvent).
- Releases, board, tracking and "my work" use categories, so custom statuses
keep working. Status changes carry name/category metadata for history.
- GraphQL: status/type/area are strings (breaking for clients sending enum
literals); Project.settings, updateProjectSettings, statusCategory,
myIssues(categories). REST: PUT /projects/{project}/settings, category filters.
- Read model: statusCategory on issues (backfilled at startup).
Statistics («Estadísticas» tab):
- New issue_timeline projection; pure functions compute created vs resolved,
cumulative flow, cycle/lead time (avg + p85), throughput per person, QA
pass/fail and release burnup. GraphQL Project.analytics(days),
Release.burnup; REST /projects/{project}/analytics, /releases/{id}/burnup.
- Web: SVG charts with validated light/dark palette, crosshair tooltip,
keyboard navigation, legend and data table.
Home "Mi trabajo": my open tickets across projects grouped by category (QA
failures first), tickets waiting for QA, upcoming releases and "Ir a…".
Tests: domain/application specs, e2e scenario for settings and analytics,
facade tests. Docs, READMEs, schema.gql/openapi.json and Word/PDF updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>