PUT /api/members/:memberId/password — admin-only, hashes and sets the
password immediately instead of going through the email reset-link
flow. Useful in this deployment where SMTP isn't configured yet, and
more generally whenever the member needs to log in right away.
Frontend: "Passwort direkt setzen" dialog in the members list dropdown,
alongside the existing "Passwort-Mail senden" (renamed for clarity now
that there are two options).
Verified end-to-end: new password logs in, old password is rejected.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
AcceptInvitationPage's form always initializes password to "" via
react-hook-form defaultValues, even when the invited email already has
an account and the password field is hidden (requiresPassword=false).
The empty string was sent as-is instead of omitted, tripping the
backend's password.min(8).optional() — which only treats an absent key
as optional, not an empty string — and surfacing Zod's raw untranslated
message ("Too small: expected string to have >=8 characters") instead
of a friendly one.
Frontend: omit the password key entirely from the payload when
requiresPassword is false. Backend: defense in depth — treat an empty
string the same as omitted via a transform, and give the min(8) check
an actual German message for if this path is ever hit another way.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
BUG-27: POST /api/chat/token required no organizationId and just took
whichever provisioned membership row the DB returned first — a user in
more than one org (multi-tenant is explicitly in scope per PRD) could
get a Matrix token for the wrong org's identity. organizationId is now
required, threaded through fetchChatCredentials/useTeamChat/
useTeamUnreadCounts, and the frontend credential cache is now keyed
per-org so switching orgs doesn't reuse a stale token. Same ambiguity
fixed in GET /api/teams' BUG-13 membership lookup. Verified: token
request without organizationId now 400s.
BUG-26: the JWT lived only in useState, so every reload or deep link
threw the user back to /login. auth-context.tsx now persists it to
localStorage and restores/validates it on mount (via /api/auth/me);
ProtectedRoute waits on a new `initializing` flag instead of redirecting
before the restore attempt resolves.
BUG-28: TeamChat's fixed vh-fraction height could never guarantee the
composer stayed on screen — page header, the create-team card and the
team-tile grid all sit above it, so total content exceeds 100vh
regardless of the chat box's own height. Switched vh to dvh (mobile
keyboard/chrome resize) and added a scrollIntoView on mount, which
fires reliably since Radix Tabs only mounts the active tab's content.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
First attempt checked err.code, which is undefined — drizzle-orm wraps
the raw pg error (the one that actually has .code = '23505' for a
unique violation) in .cause. Live-tested and caught before assuming
the fix worked from code review alone. Now checks err.cause?.code
first, falls back to err.code.
Verified: first add 201, duplicate add 409 "Member is already on this
team" instead of 500.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
Org admins can now trigger a password reset for a member from the
members list dropdown, without ever seeing or setting the password
themselves — POST /api/members/:memberId/reset-password sends the
same reset-link email the self-service /forgot-password flow uses.
Extracted the shared token-generation + email logic into
lib/password-reset.ts so both flows use one implementation. Returns
409 member_has_no_account for members who never accepted their
invitation (no linked login account to reset).
Verified end-to-end on talkhub: admin trigger sends the mail (SMTP
not configured in this test env, confirmed via the mail service's
own log fallback), non-admin gets 403.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
BUG-25: DashboardPage header ran ~200px past the viewport at 375px,
"Abmelden" unreachable without horizontal scroll. flex-col/flex-wrap
so it stacks on narrow screens instead of overflowing.
BUG-23: the previous fix (logout_all admin endpoint) never worked —
that route doesn't exist on Synapse (404, swallowed by try/catch).
A second attempt (list + bulk-delete devices) also did nothing,
because loginAsUser()'s admin puppet-login issues a deviceless token
that never appears in the devices list. Live-tested both failures via
account/whoami with the "revoked" token still returning 200.
Actual fix: revokeAccessToken() calls the plain Client-Server /logout
endpoint authenticated as the token being revoked, not as the backend's
admin token — the only mechanism that invalidates a deviceless token.
invalidateMatrixAccessToken() now returns the token it removed so the
route can pass it along. Verified: whoami returns 200 before logout,
401 M_UNKNOWN_TOKEN with the same token right after.
QA round 4 (first with real cross-browser access — Chromium/Firefox/
WebKit at 375/768/1440px) found no remaining Critical or functional
High findings once these two land: production-ready.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
Every previously-reported bug from the three QA rounds is now addressed
(BUG-11 and BUG-15 mitigated rather than fully solved — see spec for
why). Verified end-to-end on talkhub/matrix after each change.
Security:
- BUG-21: @fastify/helmet registered, CORS origin allowlist (APP_BASE_URL
+ localhost:5173) replaces `origin: true`. Verified: own origin gets
CORS + HSTS/nosniff/X-Frame-Options headers, foreign origins get none.
- BUG-13: GET /api/teams only returns matrixRoomId for teams the
requester actually belongs to (was leaking every team's room id to
any org member). Verified with/without team membership.
- BUG-14: leftJoin now filters scope='team'; new partial unique index
chat_rooms_team_id_team_scope_unique enforces one room per team at
the DB level, not just in the query (migration 0002).
Reliability:
- BUG-5: use-team-chat.ts's setup poll now backs off exponentially
(5s->60s) instead of a fixed 15s; SyncState.Error stops the client
and reconnects with backoff instead of letting matrix-js-sdk retry
against Synapse forever.
- BUG-6: one-off backfill script (src/scripts/backfill-history-visibility.ts)
sets history_visibility=shared on rooms created before that fix;
run against talkhub/matrix.
- BUG-9: route rate limit on GET /api/chat/room, unified 404 for
nonexistent-team vs not-a-member (was a cross-org existence oracle),
LRU-correct eviction order in matrix-token-cache.ts, scroll position
preserved on "load older messages", and eslint.config.js added (lint
never ran on this feature before — flat config was simply missing).
- BUG-15 (mitigated): a small module-level credential cache in
lib/matrix.ts lets useTeamChat and useTeamUnreadCounts share one
fetched token instead of each hitting POST /api/chat/token
independently. Reduces but doesn't eliminate rate-limit risk under
rapid team switching (no in-flight dedup between the two hooks).
- BUG-16: refreshCounts() checks `cancelled` before setCounts.
- BUG-17: use-team-chat.ts uses getRoomUnreadNotificationCount()
instead of the deprecated getUnreadNotificationCount(); left
unchanged in use-team-unread-counts.ts on purpose, since importing
NotificationCountType there would undo BUG-11's bundle-splitting fix.
Cleanup:
- BUG-10: new POST /api/chat/logout invalidates the cached token and
calls Synapse's logout_all admin endpoint; frontend calls it
best-effort on logout and clears its own credential cache.
- BUG-22: trim() on displayName construction (trailing space with an
empty last name); the reported trailing-dot localpart is moot after
BUG-20 since localparts are random now, not name-derived.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
toLocalpart() built vorname.nachname from real names, so anyone could
construct another org's Matrix ID and DM them directly — completely
sidestepping the user_directory.enabled=false mitigation from BUG-2,
which only blocks *searching*, not addressing a known ID. Live-exploited
during QA round 3 (cross-club DM succeeded).
Now generates a random `u<16 hex chars>` localpart with no relation to
the member's name. Display name (passed separately to createUser) still
shows the real name in room member lists.
sync-worker.ts and invitations.ts both used to retry on collision by
appending a numeric suffix to the name-derived base; since there's no
name relation left, collision retry now just draws a fresh random id.
Verified end-to-end on talkhub/matrix: new org creator got
@u747fcd631af6db20:matrix.perlbach24.de instead of a name-derived id.
Test data cleaned up.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
BUG-18 (Critical) — invitations.ts never joined the Matrix room for
members invited with a teamId (only teams.ts's add-member path did).
Standard onboarding flow left every invited team member stuck on
"Raum wird eingerichtet" forever, with no self-healing (reconciliation
only checks user provisioning, never room membership) and no repair
path (blocked by BUG-19). Now mirrors teams.ts: inline addUserToRoom
with an enqueueSyncJob("join_team_room") fallback.
BUG-19 (High) — no global Fastify error handler meant an uncaught
error (e.g. a duplicate-key insert) echoed back the raw Drizzle error,
including the full SQL statement and bound parameters, to the client.
Added app.setErrorHandler(): 5xx is logged server-side and replaced
with a generic message, 4xx keeps its already-safe original message.
BUG-12 (High) — use-team-unread-counts.ts had no SyncState.Error
handling, so an expired token meant matrix-js-sdk retried /sync
against Synapse forever with a dead token while badges silently
froze. Now stops the client and reconnects with a fresh token,
capped exponential backoff.
BUG-11 (High, partially mitigated) — deferred the app-level unread
client's start to requestIdleCallback instead of firing synchronously
on TeamsPage mount, taking it off the critical path. Does NOT fix the
underlying complaint (SDK still loads for anyone who visits the team
list, whether or not they open chat) — documented as an open
architecture question rather than declared fixed.
All four fixes verified end-to-end against the live talkhub/matrix
TLS deployment (invitation accept -> joined_rooms; duplicate insert ->
generic 500). Test data from QA round 3 and this verification pass
cleaned up (orgs, users, Matrix accounts erased, rooms purged).
Not addressed: BUG-20 (predictable Matrix IDs undermine the BUG-2
user_directory mitigation), BUG-5/6/7/9/10/13/14/15/16/17/21/22.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
Nginx Proxy Manager now terminates TLS for matrix.perlbach24.de and
talkhub.perlbach24.de with real Let's Encrypt certs. talkhub's backend
.env updated (SYNAPSE_PUBLIC_BASE_URL, APP_BASE_URL). Full chat flow
re-verified end-to-end against the public HTTPS domains: login, token
mint, room resolve, message send — all 200s, event id returned.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
BUG-4 (High, failed acceptance criterion) — per-tile unread badges:
new use-team-unread-counts.ts runs one app-level Matrix client (separate
from useTeamChat's detail-view client) that syncs every room GET /api/teams
now resolves (matrixRoomId added via leftJoin on chat_rooms) and reports
unread counts per team. TeamsPage tiles show the badge without the chat
tab being open. Side fix: createMatrixClient() now dynamic-imports
matrix-js-sdk instead of a static import, so the new always-mounted hook
doesn't drag the SDK (and its 7.8MB crypto WASM) back into TeamsPage's
main bundle — verified via build output (TeamDetail chunk 853KB -> 27KB,
SDK now in its own on-demand chunk).
BUG-3 (High) — kick-while-open: use-team-chat.ts now listens for
RoomEvent.MyMembership; leaving/removal flips status to a new "removed"
state instead of leaving history and the composer usable.
BUG-2 (High, partially mitigated) — minted tokens are unscoped Matrix
credentials by nature (Matrix has no token scoping). Disabled
user_directory search on Synapse (config + live matrix.perlbach24.de
instance), verified empty results instead of cross-club user listing.
Direct addressing by known Matrix ID is still possible — full tenant
isolation needs an explicit decision (one Synapse per club vs. an
isolation module on a shared homeserver), documented as an open item
in the spec rather than silently declared fixed.
BUG-1 (Critical, not fixed) — backend/.env.example is outside my
permission sandbox (can't read or write .env* files). Documented the
required block in the spec for manual addition; talkhub's real .env
already has the values.
Verified end-to-end on talkhub/matrix test LXCs. Not addressed in this
pass (lower priority per QA): BUG-5/6/7/8/9/10.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
Frontend: matrix-js-sdk client per opened team, /sync against Synapse
directly (no chat traffic proxied through the backend), timeline with
day-grouping and read receipts, history backfill, lazy-loaded chat tab
in TeamsPage's team detail view (keeps matrix-js-sdk out of the initial
bundle).
Backend: POST /api/chat/token mints/caches a short-lived per-member
Matrix access token via Synapse's admin puppet-login (1h TTL, in-memory
cache with 5min pre-expiry refresh, concurrent-request dedup, audited
on real mints only); GET /api/chat/room resolves a team's Matrix room.
Cached tokens are invalidated immediately on member suspend/leave.
Fixes found while wiring this up:
- createRoom() now explicitly sets history_visibility=shared (PROJ-9)
instead of relying on the private_chat preset's implicit default.
- POST /api/organizations never provisioned a Matrix account for the
org creator (only the invitation-accept path did) — they'd have
seen "Chat wird eingerichtet" forever. Now enqueues the same
provision_matrix_user job invitations use.
Open: SYNAPSE_PUBLIC_BASE_URL needs documenting in backend/.env.example
(blocked by local permission rules on that file — added manually on
the talkhub test host instead), per-tile unread counts need an
app-level Matrix client instead of one per opened team, and Synapse
needs TLS + public reachability before this works outside a LAN.
Verified end-to-end on the talkhub/matrix test LXCs: registered a
throwaway account, created org → department → season → team, joined
the team, minted a token, sent a message directly to Synapse, read it
back from room history.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
Direct browser-to-Synapse connection via matrix-js-sdk, backend only
mints a short-lived per-member access token (no chat proxy, no new
WebSocket server, no new DB table). Status set to In Progress.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
findMembership() and GET /api/organizations/mine ignored members.status —
a suspended or left member kept full API access to the organization (member
list, teams, roles, audit log) and still saw it in the org switcher, even
though only their Matrix account was deactivated. Both now require
status = "active".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
- DELETE /api/members/:memberId — admin-only, erases the Matrix account,
audit-logs before deleting (audit_logs.targetId is unconstrained text so
the entry survives), relies on existing cascade FKs for team/role cleanup.
Frontend: confirm dialog in the members table dropdown.
- POST /api/auth/forgot-password + /api/auth/reset-password/:token —
token-based reset via the existing mail.ts SMTP client, generic response
regardless of whether the email is registered (no enumeration leak),
60min token TTL, single-use. Frontend: forgot/reset password pages,
link added to the login form.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
- CLAUDE.md header/description now match actual project (TalkTime), not starter-kit template
- Retro-documented already-built MVP features as PROJ-1..PROJ-10 in features/INDEX.md
- Corrected PROJ-9: Matrix User Provisioning, room sync and reconciliation were already implemented, not just health-check
- Added PROJ-11 requirements spec for basic chat (Matrix Client-Server API, team scope, text-only MVP)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
Own installer instead of relying only on the external bashclub script:
builds homeserver.yaml straight from our homeserver.yaml.sample (so it
inherits TalkTime's security defaults — no federation, no public
registration — automatically), sets up Postgres, a systemd service, and
prints the admin token ready to paste into backend/.env. Works identically
in a Proxmox LXC and on bare metal (both are just a Debian userland).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBf3yfnnHzMQ5qhLV2285A
- Fixed leftover chat.vereinschat.local defaults in docker-compose.yml and
homeserver.yaml.sample that the earlier bulk rename missed (placeholders
weren't plain "VereinsChat" text)
- Generalized example prod domain from chat.verein.de to chat.example.org
- Disabled federation by default in homeserver.yaml.sample (client-only
resources, empty federation_domain_whitelist) since TalkTime runs a
private homeserver per deployment, not a public Matrix participant
- Added concrete nginx TLS reverse-proxy config and a federation-enable
note to the Synapse README instead of a stale "see deployment.md" pointer
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBf3yfnnHzMQ5qhLV2285A
- PATCH /api/members/:id/status (suspend/reactivate/leave) with Matrix deactivation
- Reconciliation worker comparing local member status against Synapse state
- Roles page (assign/revoke) and audit log page on the frontend
- Dashboard tiles for departments/teams now link to their pages
- Reference bashclub/zamba-lxc-toolbox as a Proxmox-LXC autoinstaller option in Synapse docs
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBf3yfnnHzMQ5qhLV2285A
- Generic org/team/member data model (renamed from club-specific naming)
- Auth, organizations, invitations with mail + Matrix provisioning
- Teams with automatic Matrix room creation, sync-job retry, reconciliation
- Departments, seasons, roles (RBAC with privilege-escalation guard), audit log
- Dashboard, members list, org switcher on the frontend
- Synapse setup docs (Docker + bare-metal) and generalized PRD/architecture docs
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBf3yfnnHzMQ5qhLV2285A
Replace Next.js/Supabase/Vercel stack with a self-hostable setup: Vite+React
frontend, Fastify+Drizzle+PostgreSQL backend with own JWT auth, deploy via
git pull + rsync + systemd (checkout/service separated, updates pulled from
Gitea). Adds a reference auth route (register/login/me) as the canonical
pattern for new routes. Updates all skills/rules/agents/docs to match.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BMdBJfD9TT6XUU2NqGG1PN
- Add paths frontmatter to frontend, backend, and security rules so
they only load when editing relevant files
- Add mandatory new-project detection to general rules that redirects
to /requirements before any implementation
- Add write-then-verify protocol for feature tracking updates to
prevent hallucinated file edits
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the manual "read .claude/agents/*.md" workflow with native
Claude Code features for a more efficient, scalable development experience:
- **Skills** (.claude/skills/): 7 auto-discovered slash commands
(/requirements, /architecture, /frontend, /backend, /qa, /deploy, /help)
with forked sub-agents for heavy tasks and inline execution for interactive ones
- **Rules** (.claude/rules/): 4 modular rule files (general, frontend, backend,
security) auto-applied based on file context
- **Sub-Agents** (.claude/agents/): Lightweight configs for frontend-dev,
backend-dev, and qa-engineer with model, tool, and turn limit settings
- **Context Engineering**: Layered context loading, context isolation via
forked skills, built-in context recovery after compaction, and
"always read, never guess" rules to prevent hallucinated code references
- **CLAUDE.md**: Auto-loaded project context replacing PROJECT_CONTEXT.md
- **Feature tracking**: features/INDEX.md as persistent state across sessions
- **Production guides**: docs/production/ for error tracking, security,
performance, database optimization, and rate limiting
- **Init Mode**: /requirements detects empty PRD and bootstraps full project
setup (PRD + all feature specs) from a single project description
Removed: 6 monolithic agent files, PROJECT_CONTEXT.md, HOW_TO_USE_AGENTS.md,
TEMPLATE_CHANGELOG.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
globals.css:
- Add all shadcn/ui CSS variables in HSL format (without hsl() wrapper)
- Include: card, popover, primary, secondary, muted, accent, destructive
- Include: input, ring, radius, chart colors
- Complete light and dark mode definitions
tailwind.config.ts:
- Map all CSS variables with hsl(var(--...)) wrapper
- Add borderRadius config for --radius variable
- Add chart colors mapping
This ensures all shadcn/ui components render correctly out of the box.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add missing --border CSS variable for light and dark mode
- Add border color to Tailwind config using hsl(var(--border))
- Fix import paths in sidebar.tsx (@/hooks/ instead of @/components/hooks/)
- Fix import path in toaster.tsx (@/hooks/ instead of @/components/hooks/)
These fixes ensure npm run dev / npm run build work out of the box.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add critical section: always check shadcn/ui before creating components
- List all 35 installed shadcn components with categories
- Add VERBOTEN section: never create own versions of shadcn components
- Update example component to use shadcn Card/Button/Badge
- Add shadcn checklist items before marking work as done
This ensures Claude Code always uses shadcn/ui instead of building
custom implementations of standard UI elements.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Revert accidental removal of design-vorgaben section while keeping
the new Supabase auth best practices.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extend QA Engineer responsibilities to include security analysis
and permission checks with a Red Team pentester approach.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- New Phase 5: Update PROJECT_CONTEXT.md with current status and roadmap
- Updates "Aktueller Status", "Features Roadmap", optionally "Vision"
- New checklist item ensures PROJECT_CONTEXT.md stays current
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- New workflow step: Check for design files before implementing
- Interactive questions for style, colors, inspiration
- Uses AskUserQuestion for structured input
- New checklist item for design clarification
Also: Refactored TEMPLATE_CHANGELOG to compact bullet-point format
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Enforce Single Responsibility Principle for feature specifications:
- Each feature file should be one testable, deployable unit
- Clear rules on what NOT to combine in one file
- 5 decision criteria for splitting features
- Dependency documentation between features
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
BREAKING CHANGE: Removed FEATURE_CHANGELOG.md and test-reports/
**What changed:**
- All 6 agents now use Git commands to check existing features/components
- Feature specs include test results and deployment status (no separate files)
- Git commits are the single source of truth for implementation details
- Git tags for deployment versioning (e.g., v1.0.0-PROJ-1)
**Why:**
- Prevents context bloat (no growing FEATURE_CHANGELOG.md)
- Scales better (Git is built for large projects)
- No manual changelog maintenance
- Better developer experience (native Git workflow)
**Migration:**
- Requirements Engineer: Uses `ls features/` and `git ls-files` instead of FEATURE_CHANGELOG
- Solution Architect: Uses `git ls-files src/components/` to check existing code
- Frontend/Backend Devs: Use `git log --grep="PROJ-X"` to see feature history
- QA Engineer: Adds test results directly to feature spec
- DevOps: Updates feature spec with deployment status + creates Git tags
**Files changed:**
- Updated all 6 agent instructions (.claude/agents/*.md)
- Deleted FEATURE_CHANGELOG.md
- Deleted test-reports/ folder
- Updated PROJECT_CONTEXT.md (removed references)
- Updated README.md (v1.4.0, updated workflow)
- Updated features/README.md (new format with QA + Deployment sections)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>