Commit graph

147 commits

Author SHA1 Message Date
6afd419dd3 test(roadmap): reschedule auditing and role scoping 2026-09-17 07:19:54 +02:00
9a5f6ca3f7 feat(roadmap): planning page with bands, timeline and stats 2026-09-17 07:19:00 +02:00
ecf5f0c173 feat(roadmap): reschedule from the timeline, always audited 2026-09-17 07:18:20 +02:00
bed7af848e feat(roadmap): Gantt-style timeline with today marker and tracks 2026-09-17 07:17:58 +02:00
bc8cc9f776 feat(roadmap): Now/Next/Later view 2026-09-17 07:17:35 +02:00
6e231374e2 feat(roadmap): loader scoped by the caller's own read rules 2026-09-17 07:17:18 +02:00
b164f3273c feat(roadmap): checklist-derived completion percentage 2026-09-17 07:16:57 +02:00
1c24687dae feat(roadmap): time axis, month ticks and greedy row packing 2026-09-17 07:16:47 +02:00
0454abb853 feat(roadmap): Now/Next/Later derived from board order and dates 2026-09-17 07:16:24 +02:00
d88dc91d7d docs: Phase 5 spec and plan for the planning and roadmap view
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>
2026-09-17 07:14:47 +02:00
8a2fb46788 test(intake): server-decided fields and project scoping 2026-09-16 18:07:16 +02:00
5a9d1cb97f feat(card): show the intake brief and transcript 2026-09-16 18:05:42 +02:00
d5cf51c944 feat(intake): wizard on the board with a plain-box fallback 2026-09-16 18:05:20 +02:00
58be48a4b0 feat(intake): brief confirmation before submission 2026-09-16 18:04:53 +02:00
04e78aef6d feat(api): intake route creating an Inbox card from a brief 2026-09-16 18:04:44 +02:00
05ce52a6bf feat(schema): card brief, transcript and size band 2026-09-16 18:04:22 +02:00
0c25a3e6a1 test(intake): cap enforcement, brief whitelist, project scoping 2026-09-16 17:56:37 +02:00
f05a2a2ed2 feat(intake): interview action and client-facing wizard 2026-09-16 17:56:06 +02:00
a74f3e79b5 feat(intake): stateless interview service 2026-09-16 17:55:36 +02:00
bac4694ab1 feat(intake): whitelist brief parsing that degrades to raw answers 2026-09-16 17:55:14 +02:00
1c6e739b16 feat(intake): question and brief prompts 2026-09-16 17:54:54 +02:00
04f38ee114 feat(intake): size band derived from decomposition and known areas 2026-09-16 17:54:39 +02:00
95c4c6c8fa feat(intake): question cap and transcript sanitisation 2026-09-16 17:54:27 +02:00
0c4bbd951f docs(plan): Phase 4a and 4b for the client intake wizard
4a builds the stateless interview. Every constraint that matters is
enforced in code rather than requested in the prompt: the seven-question
cap, the answer length, and what fields a brief may contain. parseBrief is
a whitelist, not a parser — a brief asserting priority or accepted loses
those at the boundary and they never reach a caller that could act on them.

4b adds a separate intake route rather than extending Plan 1a's client
card route. That route's tests were written against a two-field body;
widening the shape it accepts would leave them passing while proving less.
Both routes set column, priority, accepted and created_by_role themselves,
and both are separately proven to.

The property both plans are written to: the worst a hostile brief can
achieve is a strangely-worded card in the Inbox.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 17:46:54 +02:00
d928e7fd9e docs(spec): Phase 4 design for the client intake wizard
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>
2026-09-16 17:37:57 +02:00
de8ae83dcb test(narrative): no invention, and raw-subject fallback 2026-09-16 16:26:29 +02:00
ee65f2238d feat(admin): manual narrative generation 2026-09-16 16:25:58 +02:00
4a278fa56b feat(dashboard): recent work in plain English 2026-09-16 16:25:39 +02:00
8711c36833 feat(card): progress narrative and humanised commit history 2026-09-16 16:25:19 +02:00
30446153c8 feat(ai): card narratives regenerated when the commit set changes 2026-09-16 16:24:38 +02:00
7ca2798561 feat(schema): card narrative fields 2026-09-16 16:24:18 +02:00
5cecc87010 feat(ai): narrative prompt and set-based staleness 2026-09-16 16:23:32 +02:00
aea000f654 test(ai): merge exclusion, graceful degradation, summary persistence 2026-09-16 16:18:40 +02:00
cff4f9c7fd feat(admin): summary progress and manual generation 2026-09-16 16:16:56 +02:00
6890a6d29e feat(ingest): summarise new commits after metrics 2026-09-16 16:16:27 +02:00
bf7d024cef feat(ai): commit summarisation service that never throws 2026-09-16 16:16:08 +02:00
8addf87eef feat(ai): index-matched batch prompting and parsing 2026-09-16 16:15:42 +02:00
bce71a666a feat(schema): commit summary fields 2026-09-16 16:15:18 +02:00
2401dbd68f test(ai): settings page never exposes the API key
Three tests cover the storage and access boundary:

- A client manager cannot list the settings collection at all (rules are
  superuser-only — verified by the rejection, not by silent skip).
- The settings page HTML returned to the owner does not contain the key
  value, even when the key is freshly set to a unique sentinel.
- A client manager gets 404 on /admin/settings (admin layout gates on
  role, not on session presence).

helpers.ts gained ownerEmail / ownerPassword — needed by the settings test
and reusable for any owner-only integration test going forward.
2026-09-16 15:59:49 +02:00
70cb355762 feat(ai): settings page with a real connection test 2026-09-16 15:54:58 +02:00
e837500c6f feat(ai): single point of provider selection 2026-09-16 15:53:59 +02:00
b233259f18 feat(ai): OpenAI-compatible adapter for MiniMax 2026-09-16 15:53:34 +02:00
b172079912 feat(ai): Anthropic adapter 2026-09-16 15:52:56 +02:00
79fa035eb0 docs(plan): Phase 3a, 3b and 3c for AI humanisation
3a ports the provider structure from MEGAIFRASTRUCTURE's chat/ — SDK-free
types, one adapter per provider — but deliberately not its interface: this
phase needs text in, text out, not a chat transcript and tool protocol. It
carries forward the MiniMax details that module learned the hard way: the
native /v1 route rather than the /anthropic shim, an Open Platform key, and
stripping the inline <think> blocks M2.x emits.

3b is built around one property: batch responses are matched to commits by
an echoed index, never by position. A single skipped line under positional
matching would attach fluent, confident, wrong sentences to every later
commit, with nothing raising an error.

3c is the first plan whose output a client reads, so its verification step
is to read a narrative as the client would and check it claims no
completion, states no date, and invents nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 15:52:32 +02:00
f3e4b30d46 feat(ai): settings storage with a browser-safe projection 2026-09-16 15:52:12 +02:00
8f1ac43e98 feat(ai): strip MiniMax inline reasoning blocks 2026-09-16 15:51:37 +02:00
b5593457af feat(ai): non-editable preamble with an append-only owner prompt 2026-09-16 15:51:15 +02:00
44bcc81d97 feat(ai): provider-neutral completion interface 2026-09-16 15:50:39 +02:00
76be7440a8 feat(dashboard): remove the 'estimated' pill from the Hours KPI
The methodology panel already explains that hours are estimated from
commit timing — the pill on the tile was redundant. With the pill gone
the tile row reads as four plain figures, and the methodology is the
single place a client goes to interrogate any of them.
2026-09-16 15:38:42 +02:00
6c391e04a3 fix(dashboard): month bars render with definite-height parent
The chart column was flex-1 inside h-40, but had no explicit height of
its own. The bar inside used height: X% which has nothing to compute
against when the parent is auto-sized — Tailwind's percent utilities
collapse to 0 without a definite parent.

Wrap the bar in a flex-1 container that consumes the remaining vertical
space. The percentage now resolves against that container's actual height,
so the 154h August bar fills three-quarters of the way up the column,
the 5h May bar barely peeks above the baseline, and the rest sit between.
2026-09-16 15:36:50 +02:00