The design spec said an unset token gets a 401 like a missing/wrong one;
the implementation (and the plan's own Definition of Done) has always
treated unset/misconfigured as 500, matching CRON_SECRET's own behavior.
Also extends the .env.example comment to spell out that this same value
must be set in Coolify's environment too, not just .env.local — this app
has no local PocketBase, so the token is checked against process.env on
whichever environment actually receives the request.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Design for a bearer-token-authenticated REST API letting an external
AI coding agent read every project/card the owner has and write back
exactly three things: a comment, subtasks, or a column move. Follows
the same auth pattern as the existing CRON_SECRET-protected route.
Agent-authored comments are prefixed so they're never mistaken for
something the owner typed, mirroring Phase 7's "Logged" tag principle.
MCP explicitly deferred to a later, thinner follow-up.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Any signed-in user — owner or client_manager — can now edit
dates directly on the Gantt. Three interaction shapes:
- Drag the bar body to move it (both start and target shift
by the same number of days, snapped to whole UTC days).
- Drag the right edge to resize (target only).
- Double-click any card or subtask bar to open a modal with
Start + End date inputs, ESC to close, Enter to save.
Both card and subtask rows ship the same affordances. Cards
go through the existing rescheduleCardAction so the
activity-log row keeps recording who moved the date;
subtasks go through a new setSubtaskDatesAction (no audit
row — matches the existing subtask-toggle pattern).
Components touched:
- GanttBar gains canEdit / onMove / onResize / onEditRequest.
- GanttDateEditor is the inline two-input modal.
- ProjectSection forwards four callbacks (card move/resize,
subtask move/resize) plus onEditRequest.
- GanttPlanView holds the editor's open state and computes
one shared timeline range across sections.
- GanttClient wraps the view and bridges to the actions.
- /plan/page.tsx renders GanttClient.
Spec §6 amended to move drag from out-of-scope into the
delivered surface.
482/482 unit tests pass (was 471, +11 from bar-math).
Integration plan-gantt tests still pass.
Build clean.
Redesign the planning page as a project-grouped Gantt:
parent rows are cards (start_date..target_date), indented
children are subtasks (gaining optional start_date and target_date).
No new collections. cards gains an optional assignee text field.
Replaces the Phase 5 Puzzle + Timeline tabs at /plan.
Out of scope: drag-to-reschedule, subtask dates edited on the
card page, connector lines, status/priority filters.
The branch plan-puzzle-v1-tests had accumulated changes from a
sibling thread (Plan Puzzle v1 backend + frontend) that hadn't
been committed yet, plus a new src/lib/git/repo-url helper used by
the Forgejo ingest path. Pick them all up so the working tree is
clean and the branch reflects everything since its fork.
Files (Plan Puzzle):
src/app/actions/client-proposal.ts
src/app/actions/owner-plan.ts
src/app/actions/reschedule.ts
src/app/actions/reschedule-helpers.ts (+ test)
src/components/admin/project-form.tsx
src/components/roadmap/roadmap-builder.tsx
src/components/roadmap/timeline-view.tsx
src/lib/cards/write-with-audit.ts (+ test)
src/lib/roadmap/timeline.ts (+ test)
New helper:
src/lib/git/repo-url.ts (+ test)
Verified: npm run build clean, 466/466 unit tests pass.
Spec changes from feedback:
1. §5.1 — the picker description was written as if the user could
see the rendered HTML. Replaced with an ASCII sketch and a
stronger 'no internal concepts reach the rendered surface' line.
2. §7 — added a binding label-mapping table for every owner-facing
term. The internal names stay (in the DB, in code, in the spec);
only rendered text changes. Soft-language variants:
intake_links -> 'Request link'
Mint a link -> 'New request link'
Per-link daily cap -> 'Daily cap'
Revoke -> 'Pause'
Re-activate -> 'Resume'
show_in_intake -> 'List on request page'
Phase 9 design doc. Same format as Phase 8. Headline:
- intake_links.client replaces intake_links.project for new rows;
old shape stays live
- projects.show_in_intake default false; owners opt in per project
- /intake/<token> becomes a picker page; selecting a project
redirects to /intake/<token>/p/<projectId> for the wizard
- daily cap is now per (link, project) via a new
intake_link_submissions collection
- Phase 8 invariants (worst a hostile body achieves is a
strangely-worded card; same 404 wording for unknown/revoked/
expired; server decides created_by_role=anonymous; parseBrief
whitelist) all carry forward unchanged
Three plans in 9a/9b/9c. ~3 working days.
Design for letting the owner record off-repo work as a commits row
tagged source=manual, reusing every existing commit render path
(recent work, card history, activity/monthly aggregation) instead of
a parallel system, with a declared-hours figure added to the total
rather than session-clustered, and a small "Logged" tag distinguishing
it from a real commit everywhere it appears.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Design for grouping projects by client on the home page and in a new
sidebar Kanban flyout tree, making the dashboard the project landing
page with the board one click away, and a prototype/project kind flag
so unsolicited client-facing prototypes can live alongside committed
projects with the same permissions, just tagged differently.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two views rather than one chart, because the data forced it: measured on
the live database, 1 of 5 live cards has a target date and none has both
dates. target_date is client-owned and optional; start_date is only set on
Inbox acceptance. A conventional Gantt would render almost empty.
So Now/Next/Later is the default and works with zero dates, and the
timeline earns its place as clients set dates. From Dave's Gantt reference
it takes the quarter header, the Today marker, project grouping with the
client's colour, and the per-row completion percentage — which maps onto
checklist progress, the one honest source for it. The faint full-width
track is load-bearing rather than decorative: it makes a sparse chart read
as unscheduled instead of broken.
Drag-to-reschedule always writes an activity row naming the owner, because
target_date belongs to the client. A developer silently moving a date the
client chose is the failure the activity log exists to prevent.
Completes the plan set for phases 1 through 5.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A guided interview that produces a written brief with sub-tasks, landing
in the Inbox as a draft through Plan 1c's existing accept flow.
Corrects an over-promise made during the Phase 1 discussion: the wizard
was described as grounding date proposals in measured per-card velocity,
and that data does not exist — Plan 2c deferred per-card effort because
session padding does not sum correctly across cards. This phase therefore
produces no time estimate at all, only a size band.
Names the intake as the highest-risk surface in the product: it is the
only place where text written by someone other than the developer enters
a prompt whose output creates records. The security property is that the
worst a hostile brief achieves is a strangely-worded Inbox card.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
One-liners per commit and a progress narrative per card, plus the
swappable provider layer both depend on, ported from the chat/ structure
in MEGAIFRASTRUCTURE.
Model and cost are measured rather than assumed: the full 779-commit
backfill costs about ninety cents on claude-opus-5, so model choice here
is a quality decision and not a cost one. Batch API considered and
rejected — it saves roughly forty cents and adds a job state machine.
Records the rules the non-editable preamble must guarantee, each tied to
a specific way a summary could mislead a paying client.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The neutral (gray) pill read as a status badge rather than an integral
annotation. The brand tone ties the indicator visually to the brand,
making it part of the figure rather than an overlay. The meaning is the
same — 'this number is not measured, it is computed' — and the methodology
panel still explains exactly how.
Builds on the Hermes build log Dave produced by hand from WFDASH-WEBAPP,
adopting its metric set, activity calendar and workdays-not-calendar-days
framing, and correcting three things: the "6 contributors" who were one
person plus AI agents, two undocumented hour methodologies, and monetary
framing that suited an internal audience but not a client-facing page.
Settles the effort methodology with named constants shown on the page,
excludes merge commits (verified: 787 total, 779 non-merge, 8 merges), and
requires HMAC verification on the webhook endpoint.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Covers the data model, the field-level permission model, card ordering,
pages, and acceptance criteria for the board spine. Records the decisions
carried into Phases 2-5 (commit linking, ingestion, effort inference, AI
publishing policy) so later specs do not relitigate them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>