Skip to content

Latest commit

 

History

History
247 lines (196 loc) · 116 KB

File metadata and controls

247 lines (196 loc) · 116 KB

Project Plan — kaos-control / Innovation Maker

Living document summarising project state. Updated on every commit per the Commit Conventions in CLAUDE.md.

Current stage: Graph & artifact list filters extended with labels and priority. priority is now a first-class frontmatter field with a dedicated SQLite column, server-side filter param, graph chip-selector, and artifact list dropdown.


Recent Changes

Rolling log — add a dated bullet per commit.

  • 2026-08-10 — Ship a systemd user unit for running the server as a Linux service (packaging/systemd/kaos-control.service) plus a "Run as a service (Linux, systemd)" section in the README. A user unit (not a system unit) is deliberate: the server runs as the login user, so agent runs inherit HOME, ~/.claude credentials, git identity, and ~/.kaos-control — and the whole path is rootless (binary in ~/.local/bin, unit in ~/.config/systemd/user, and loginctl enable-linger for self is permitted by systemd's default polkit, so no sudo anywhere). Design notes: ExecStart invokes the serve subcommand rather than -d — equivalent on current builds, but pre-0.2.0 binaries reject -d with unknown flag and exit 1 (caught by smoke-testing against the installed v0.1.2); -config is pinned explicitly because the user manager doesn't reliably inherit XDG_CONFIG_HOME; Environment=PATH spells out the per-user tool dirs since agent runs shell out to claude/git/go/node and the user manager's default PATH is minimal (an optional ~/.config/kaos-control/service.env overrides it — needed here, as nvm-managed pnpm was invisible to the service and would have broken make build-web pipeline steps); TimeoutStopSec=30 covers the server's 10s graceful drain, after which the control-group kill reaps agent subprocesses. No hardening directives (ProtectSystem et al.) — agents legitimately write outside $HOME into project paths. Verified with systemd-analyze --user verify and smoke-tested end to end (enable/start/restart/stop, port release, /api/health — note /health alone falls through to the SPA).

  • 2026-07-13 — Bump VERSION 0.1.4 → 0.2.0 (renumbered — the v0.1.3→now delta is ~405 commits with new features + behaviour changes, a minor bump not a patch). Drafted RELEASE_NOTES-0.2.0.md (subdirectory artifacts, guided Open Questions resolution, DevOps run history + CLI, claude-env driver, priority/release inheritance, daemon flag, resilient agents + live queue, reliability & security hardening incl. 13 x/crypto CVEs). Wrote plans/road-to-1.0.md: why 0.x, what 1.0 promises (frozen format/config contract, proven reliability, safe-by-default agents, external users, settled name), current status against each, and a Road-to-1.0 checklist. Recommendation: stay 0.x; earn 1.0 deliberately.

  • 2026-07-13 — Bump golang.org/x/crypto 0.51.0→0.52.0 to clear 13 Dependabot alerts (7 critical) — all x/crypto/ssh advisories (auth bypass, FIDO/U2F presence-check bypass, key-constraint bypasses, DoS/deadlocks). Indirect exposure via go-git's git-over-SSH plus our direct use of x/crypto/argon2; limited real surface (not an SSH server) but critical-rated + trivial. Applied on both main (closes the alerts — Dependabot tracks the default branch) and kc-dev (independently, since the branches have diverged and can't cleanly merge). go build ./... clean.

  • 2026-07-09 — Cleared the 3 lint defects the fixed test-runner filed, and got make lint fully green. staticcheck: removed dead artifactRow/parseArtifactList/extractJSONField in devopscmd/client.go (lint-devops-cli-unused-json-helpers); removed the redundant if last != nil checks in internal/devops/logger.go backfillRecordfirst/last are set together so the existing first==nil guard already ensures last!=nil, which is what made SA5011 flag the deref (lint-logger-nil-deref-sa5011); removed unused seedRunRecord/writeMinimalLogFile + the now-unused encoding/json import in run_record_test.go (lint-run-record-test-unused-helpers). Those fixes unmasked a govulncheck stdlib advisory (crypto/tls, fixed in go1.25.12) that had been hidden behind the staticcheck failure — bumped the go.mod toolchain go1.25.11→go1.25.12 to clear it. All 5 lint stages (go vet, staticcheck, govulncheck, gosec, gitleaks) now pass. Also raised agent-runner-strip-schedulewakeup (medium): the one-shot agent runner should reject/strip ScheduleWakeup structurally rather than trusting each agent's prompt — the fixed test-runner still attempted it twice.

  • 2026-07-08 — Fix the test-runner agent (defect test-runner-parks-on-schedulewakeup). Rewrote its prompt in config.yaml to run every suite synchronously in the foreground (make lint / test-unit / test-integration / vitest / test-e2e), explicitly forbidding backgrounding and ScheduleWakeup (which the one-shot agent runner never honours), and bumped timeout_minutes 30→45 to cover e2e. Also spelled out the defect-filing format + dedup (read existing defects before creating). Config validated via the Go loader. Note: config isn't hot-reloaded (handleUpdateConfig writes without refreshing the cached p.Cfg, and config.yaml isn't watched), so a server restart is required to activate — which also makes the day's code fixes (index status-drift, dot-dir, truncated-stream race) live.

  • 2026-07-07 — Ran the QA cycle via the test-runner (devops test-all pipeline) as intended, then triaged. Key finding: the test-runner agent is broken for long suites — it runs unit + vitest, then backgrounds integration/e2e and calls ScheduleWakeup to resume, but kaos-control's one-shot agent runner never re-invokes it, so it parks (done after ~180s) and files 0 defects for the slow suites. Raised high-priority defect test-runner-parks-on-schedulewakeup (suggested fix: run suites synchronously in-foreground, drop ScheduleWakeup). Ran the abandoned suites manually: all green — Go unit 20/20, web vitest 1536/1536 (the agents cleared the earlier 20 frontend failures), Go integration ok (250s). No product-bug defects warranted this cycle; e2e not independently verified.

  • 2026-07-07 — Triage + fix the 16 defects opened by today's QA cycle. Two genuine product bugs fixed: (1) dot-dir runtime indexing (recursive-subdir-runtime-creation, dup idea-archiving-6) — watcher.shouldProcess only checked the filename's leading dot, so files in a dot-dir created at runtime (.trash/dot.md) were indexed even though addDirRecursive skipped watching the dir; now rejects any dot-prefixed directory ancestor (TestDotDirExclusion_RuntimeCreation passes). (2) Truncated-stream flaky race (supervisor-persists-metrics-flaky + queue-happypath-singleproject-flaky, same root cause) — cmd.Wait() ran concurrently with the stdout scanner and closes the StdoutPipe; a fast-exiting process lost its terminal result line → false truncated_stream. The reaper (startCommandProcess) now drains the readers before cmd.Wait(), bounded by readerDrainGrace (5s) for the detached-grandchild case; flaky pair 15/15 (was ~1-in-5). Dedup: closed 3 duplicate defects (devops badge ×3→1, dot-dir ×2→1). Re-scoped the pipeline-badge defect (devops-pipeline-run-history-10) — the badge is implemented + WS-wired, so it's E2E timing, not a missing feature (reassigned to test-developer, needs the E2E run to confirm). Left the 9 test-debt defects (5 missing tests + 4 broken frontend/mock tests) for the now-sonnet test-developer cycle. Full integration suite green; unit green.

  • 2026-07-06 — Fix "approving an artefact doesn't stick" (defect index-status-stuck-sha-guard). Root cause: IndexFile's content-hash skip-guard (internal/index/index.go) returned early whenever the stored body_sha256 matched the file — treating "content unchanged" as "row current". When the index row's status drifts from the file (observed on lifecycle/tests/idea-archiving-5-test.md: disk+git approved, index stuck draft, hash matching the approved file), the guard locked the stale status in permanently — every re-index (startup scan, watcher, and the transition's own IndexFile) skipped, while the status_transition event still fired (illusion of saving). Confirmed reproducible across a restart. Fix: the guard now also requires the stored status to match the parsed status before skipping, so the next index (transition / watcher / startup scan) self-heals the row. Regression test TestIndexFile_HealsStatusDriftDespiteMatchingHash (red without fix, green with). Origin of the status/hash desync (likely a concurrent-write / external Obsidian-sync race) left as a follow-up; the guard fix heals the symptom regardless. Deploy = rebuild + restart (startup scan then self-heals stuck rows).

  • 2026-07-06 — Switch the test-developer agent from claude-env + qwen3-coder:30b (local Ollama on leia.packsin.com) back to claude-code-cli + sonnet, matching backend/frontend-developer (timeout_minutes: 0). The local 30B model was producing consistently broken tests — every test-developer output examined recently had to be hand-fixed (exported-vs-unexported helper names, hallucinated helpers, unused imports, wrong API status codes, un-runnable fixtures) and runs were slow (17–45 min, several hitting the timeout). The failure modes (doesn't read existing code / doesn't verify assumptions) are characteristic of an underpowered model for a task needing deep codebase grounding; the Sonnet-driven roles produce working code on the same codebase. Removed the now-unused base_url/auth_token.

  • 2026-07-05 — Assessed the project-queue-view backend plan (project-queue-view-3-be): Milestone 2 is not needed — every queue job already carries a project field (internal/queue/types.go), so the frontend filters client-side (FR-2 option 1) with no endpoint, and M2 would only reshape the same snapshot without fixing the real gap. (a) Fixed project_queue_test.go (the botched M1 verification): it enqueued for unregistered project-a/project-b; M1 only needs "each job carries a non-empty project", so repointed both tests at the registered testproject using two distinct artifacts (FR3 duplicate-active suppression rejects a 2nd job for the same project+artifact). All 3 TestProjectQueue_* pass; full integration suite green. (b) Raised defect queue-events-missing-broadcasts: queue.added isn't broadcast on the normal enqueue path and queue.cancelled is never broadcast, so other clients don't update live — the real blocker behind the plan's Open Questions, orthogonal to project-scoping. Fix = two broadcastJobEvent calls (payload already carries project), which also makes the M1 client-side filter fully real-time.

  • 2026-07-05 — Unbreak the tests/integration build after a batch of agent runs left it red (same pattern as 2026-06-29). (1) go.mod — an agent (run a1244edee) committed a testify-using test (project_queue_test.go) but left the required github.com/stretchr/testify require uncommitted; committed it (matches go mod tidy, go.sum already consistent). (2) open_questions_config_test.go (run d9e44e60) used exported helper names (SeedArtifact, MakeArtifact, NewTestEnv, DoRequest, RequireStatus, ReadJSON) when every integration helper is unexported — fixed the casing; it now compiles and passes (the backend it targets — GET /config/open-questionsanswer_format, internal/config OpenQuestionsConfig — was actually implemented). Outstanding: project_queue_test.go compiles but fails at runtime (enqueues for unregistered project-a/project-b; the queue env only registers testproject) — needs a multi-project fixture, tracked separately.

  • 2026-07-05 — Harden the open-questions auto-block detector against placeholder/sentinel content (idea requirements-analyst-suppress-empty-open-questions). artifact.HasOpenQuestions already treated a truly empty ## Open Questions section as non-blocking; it now also treats a section whose only content is a "no questions" sentinel (none, n/a, na, nil, no open questions, no questions, tbd — list-marker/punctuation/emphasis-insensitive) as empty → non-blocking, while a real question (even one containing a sentinel word, e.g. "should none of the users…") still blocks. This fixes the actual failure mode: an agent emitting ## Open Questions\n\nNone wrongly auto-blocked the artefact. Added 3 unit tests; the 7 existing HasOpenQuestions tests and the internal/index autoblock tests remain green. Updated the idea with the finding (empty already handled; root cause is the requirements-analyst prompt listing Open Questions as a standard section) and marked the detector fix done; the prompt cleanup remains optional.

  • 2026-07-05 — Close defect moderate-dependency-vulnerabilities-deferred (planning → done). Its documented scope is satisfied — the two blocking CVEs (vite .map traversal, esbuild dev-server) are fixed, and the two residuals are dev/test-only with ≈zero exposure (esbuild GHSA-gv7w Deno-only/not-applicable, ws GHSA-58qx test-time only). The defect had become a stale rolling tracker; Dependabot on the default branch is now the tracker of record (the live #31–#35 set was cleared the same day).

  • 2026-07-05 — Clear five Dependabot alerts by bumping four deps on main (default branch Dependabot tracks; commit 2f2d4161), then merged main → kc-dev. vite 6.4.2→6.4.3 (dev) clears #31 (High) + #32 — both Windows-only dev-server issues, not exploitable on this macOS/Linux toolchain; echarts 6.0.0→6.1.0 (runtime) clears #33 (XSS); js-yaml 4.1.1→4.3.0 (runtime) clears #35 (quadratic DoS); golang.org/x/net 0.53.0→0.55.0 (indirect) clears #34 (HTML-parser DoS). All non-major bumps. Verified go build ./..., pnpm build, and vue-tsc --noEmit clean on both branches.

  • 2026-07-04 — Add two more tech-stack catalog artifacts requested by Keith: php-symfony-postgres (structured full-stack PHP web on Postgres) and python-mongodb (document-oriented Python; complements the relational python-fastapi entry). Wired compatibility edges from the architectures they suit — php-symfony-postgres ↔ modular-monolith/single-service-saas/local-web; python-mongodb ↔ modular-monolith/single-service-saas/serverless-faas — and updated the catalog README. Catalog now 9 architectures + 11 stacks, 61 cross-links all verified resolving. These complete the named-bundle set the original architecture-templates idea called for (Go+SQLite, PHP/Symfony/Postgres, Python/Mongo).

  • 2026-07-04 — Seed the architecture catalog for project onboarding (from Keith's research; supports the target flow pick architecture → pick stack → scaffold config/pipelines/ADRs). Refactored three monolithic research docs into per-item lifecycle artifacts under lifecycle/architecture/: 9 architecture artifacts (5 refactored — standalone-desktop, local-web, cloud-native-microservices, event-driven-streaming, edge-hybrid — plus 4 new filling the pragmatic middle: modular-monolith, single-service-saas, serverless-faas, mobile-native) and 9 tech-stack artifacts (go-vue, ts-react-nest, python-fastapi, java-spring-angular, go-grpc-microservices, tauri, wails, electron, flutter). Added architecture + tech-stack to KnownTypes (internal/artifact/artifact.go) so they index cleanly. Each architecture declares compatible stacks via related_to: (real graph edges — 55 cross-links, all verified resolving) and links sibling architectures via body wiki-links (feeds the relationship-map idea). Added a catalog README.md index; removed the three superseded monolithic docs (content fully preserved in the decomposed artifacts). Expanded architecture-templates with a full design (two new types + three-layer model, catalog-ships-in-binary/copied-on-init decision, compatibility model, selection→config wiring, questionnaire schema, filename/lineage exception). New ideas: onboarding-architecture-selection (guided questionnaire → recommendation + free browse) and architecture-relationship-map (scoped graph of architecture relationships). go vet ./internal/artifact/ clean.

  • 2026-06-29 — Repair the tests/integration package, which had been red on main: four agent-produced test files (from runs cbcdfc8…/843e959… [killed-timeout] and 7499574d…/9ce2fe6… [done]) were committed broken. (1) triage_watcher_regression_test.go called two hallucinated helpers — added readArtifactContent/writeArtifactContent to triage_helpers_test.go. (2) devops_pipeline_run_history_integration_test.go imported internal/devops unused — removed it. (3) agent_metrics_test.go's TestReportsAgentUsage_AggregatedTTFS set up TTFT fixtures it never ran and asserted 300/500 against always-0 TTFTs — now persists TTFT via SetAgentRunTTFT. (4) concurrent_run_under_contention_test.go's TestConcurrentRunUnderHighLoad fired 3 concurrent QA runs on one artifact expecting all 202, but the guard correctly returns 409 — rewrote to assert the real guard (202 → 409 → reset-to-approved → 202). Full make test-integration green (0 failures). Root cause (corrected): this is not a model-wide qwen3-coder:30b problem — that model completed 6–8 other jobs cleanly. These two runs (843e959… ran go test 6×) got stuck iterating on their own broken test code — the package couldn't build (hallucinated helpers / unused import), so the "tests pass → done" exit was never reachable and they burned to the 45-min timeout (which correctly caught them). The real gap is that kaos-control auto-commits broken intermediate output — even labelling non-compiling 7499574d…/9ce2fe6… runs [done] — so a pre-commit build/vet/test gate on produced Go files (reject the commit + mark the run failed instead of done) would have blocked all four broken files.

  • 2026-06-27 — Implement daemon-flag-usage-guide (M1–M4, plan lifecycle/backend-plans/daemon-flag-usage-guide-3-be.md). M1: bare kaos-control invocation now prints usage to stderr and exits 2 instead of starting the server. M2: added -d/--daemon flag (any position, stripped before flag.Parse); server start is now gated on -d, --daemon, or serve — bare -config <path> without -d is a usage error. M3: expanded usage const with product description, -d/--daemon flag, all eight subcommands (devops and releases were missing), and the per-command help pointer. M4: updated README.md (bare-start instructions × 3), Makefile (make run passes -d), and docs/end-to-end-smoke-tests.md to reflect the new required -d flag.

  • 2026-06-27 — Fix devops status <job-name> (defect devops-cli-list-and-status-commands M2): rewrite cmd/kaos-control/devopscmd/status.go to require a positional job-name arg, call GET /api/p/{project}/devops/pipelines/{slug}/runs?limit=1 for the most recent run record, then fetch the NDJSON run log and extract step-completion events for an output summary (step name, status, exit code, duration). Human-readable and --json modes. Updated the root devopsUsage string to reflect the new signatures.

  • 2026-06-27 — Fix devops list (defect devops-cli-list-and-status-commands M1): rewrite cmd/kaos-control/devopscmd/list.go to call GET /api/p/{project}/devops/pipelines and display a SLUG/NAME/TYPE/STEPS table. Previously the command queried lifecycle artifact metadata instead of devops pipeline definitions.

  • 2026-06-27 — Raise defect agent-auth-error-fail-fast (from investigating run 215bc5d8c2773b49: a test-developer run burned ~58 min / $1.08 grinding through Claude's 10-attempt retry budget on transient 401 authentication_failed — caused by an OAuth refresh-token rotation race between concurrent Claude clients sharing one host login, not a logout). kaos-control marks it failed only after the binary exits; it should detect api_retry 401 / authentication_failed (or the terminal is_error:true "Not logged in" result), kill the run early, and re-enqueue it (without pausing the queue — distinct from rate-limit). Recommended mitigation noted: give agents a dedicated ANTHROPIC_API_KEY via the claude-env driver so they don't share the rotating OAuth token.

  • 2026-06-26 — Add a regression test for the roadmap 3D DAG fix (tests/web/ForceGraph3D.dagMode.test.ts). Mounts ForceGraph3D with dag-mode + a 2-cycle graph and asserts the component registers onDagError before dagMode, that the handler tolerates a reported cycle without throwing, and that neither is touched when dag-mode is absent (regular-map path). Verified the test fails if graph.onDagError(() => {}) is removed. Frontend 1487/1487.

  • 2026-06-26 — Resolve 7 Dependabot alerts on main. Bumped runtime markdown-it 14.1.1 → 14.2.0 (#21, MEDIUM — quadratic-complexity DoS in the smartquotes rule; the only runtime/shipped dep of the set). Added a pnpm overrides for undici ^7.28.0 in web/package.json (#22–#28, dev-only, transitive via jsdom ← vitest) — pinned to the 7.x line (the patched 7.28.0) rather than letting it jump to undici 8.x, to stay compatible with jsdom@29. vue-tsc clean, make build-web succeeds, frontend 1484/1484 green. Dependabot tracks the default branch, so these land on main to close the alerts.

  • 2026-06-26 — Fix the Roadmap 3D graph layout (defect roadmap-3d-graph-dag-cycle). The roadmap is the only caller that enables DAG mode (dag-mode="lr") on the shared ForceGraph3D.vue, which registered no onDagError handler — so 3d-force-graph couldn't assign DAG depths on the roadmap's cyclic graph (timeline/parent/depends_on/related_to/assigned edges) and nodes piled up at the origin with overlapping labels (the engine rendered; only the layout failed). Added graph.onDagError(() => {}) before dagMode() so cycles are tolerated and the rest lays out. Verified live by the user; defect → done. (The regular 3D Map never sets dagMode, so it was unaffected.)

  • 2026-06-14 — Documentation panel verified working; closed defect doc-view-renders-raw-content (in-developmentdone). The docs view renders markdown previews, HTML in a sandboxed iframe, and images inline, and the list opens all docs in the in-app viewer (docs-panel-viewer-2 requirement already done). Release notes for 0.1.3 finalised in the GitHub release (the working-copy RELEASE_NOTES-0.1.3.md draft was removed).

  • 2026-06-14 — Fix a stale e2e assertion in flows/07-doc-new.spec.ts. The "New Docs" flow creates the originating doc via brainDump.createDoc, which sets status: 'raw' (the pre-draft quick-capture status from the raw-artefact-status feature) — but the test asserted status=draft. The app is correct; updated the assertion to expect raw. Flow 07 green 3/3.

  • 2026-06-14 — Stop gitleaks flagging its own documentation. The earlier generic-api-key false-positive fix (commit 51d72d73) quoted the matched code inside both the .gitleaksignore explanatory comment and a PROJECT_PLAN change-log entry, so the same rule re-matched those two lines (2 new "leaks"). Added their fingerprints to .gitleaksignore (without reproducing the matched string here, to avoid recursing). gitleaks detect clean again — no leaks found.

  • 2026-06-14 — Fix a stale RunDetailModal.test.ts assertion. The test expected a backdrop click to emit close, but the modal-closes-on-outside-click defect (done, KC-Release3; commit fed90a6b) intentionally disabled outside-click dismiss on agent/artifact modals — dismissal is now explicit (close button / Escape) only. The component is correct; flipped the test to assert a backdrop click is a no-op (emitted('close') undefined). RunDetailModal suite 21/21 green.

  • 2026-06-14 — Get make test-integration (the devops test-integration.yaml pipeline step) green — the only failures were the 5 docs-panel tests. 4 were test bugs: docs_list_test.go, docs_security_test.go, and docs_write_test.go (×3) read the error code at top-level data["code"], but apiError nests it as data["error"].code (every other integration test reads the nested shape) — the handler was returning the correct status+code all along. 1 was a real backend bug: docs.Read mapped any sandbox.Resolve error to ErrPathTraversal, so a GET for a file under a non-existent docs/ dir returned 400 path_traversal instead of 404 (sandbox walks above the absent root and flags traversal); added an os.Stat(docsRoot) not-found guard mirroring docs.List. All 20 docs tests pass; full make test-integration green (0 failures).

  • 2026-06-13 — Get make lint fully green (each fixed stage exposed the next). Bumped the go.mod toolchain directive go1.25.10 → go1.25.11, which clears the 2 Go standard-library govulncheck advisories (textproto / crypto/x509). That exposed gosec G704 (SSRF via taint) on the two Gemini API-client http.NewRequest calls in internal/agent/gemini.go — the request URL is built from the operator-configured base URL + model + API key, not user input, so added G704 to the Makefile gosec -exclude list with a justification comment (consistent with the existing G705 taint exclusion). That in turn exposed a gitleaks false positive: generic-api-key matched the Go identifier durApiMs = durApiMsN.Int64 in internal/reports/agent_usage.go:413; added its fingerprint to .gitleaksignore. All five lint stages (go vet, staticcheck, govulncheck, gosec, gitleaks) now pass.

  • 2026-06-13 — Clear the 6 staticcheck findings that were failing make lint: SA1029 in internal/agent/gemini_cli_test.go (string context key → local ctxKey type); S1039 in internal/http/releases.go (drop a no-arg fmt.Sprintf); U1000 unused releasesDir in internal/release/disksync.go and unused ptr64 in internal/reports/agent_usage_test.go (removed); SA4006/SA4010 in internal/triage/triage_test.go (the started slice was written but never read — added an assertion that all three runs start, which is what the concurrency test intends). go vet + staticcheck now clean. NOTE: make lint still fails at govulncheck — 2 Go standard-library advisories (textproto / crypto/x509) fixed in go1.25.11; the toolchain here is go1.25.10. That's a toolchain upgrade, not a code change.

  • 2026-06-13 — Actually fix the documentation-view rendering defect (doc-view-renders-raw-content), which a prior frontend-developer run marked done without verifying the rendered output. In web/src/views/project/DocsEditorView.vue: (1) HTMLisHtml now matches the text/html prefix (the backend's http.DetectContentType returns "text/html; charset=utf-8", so the old === 'text/html' never matched), and the iframe renders a new htmlSrc computed that atob()-decodes body_base64 (non-markdown responses carry body_base64, not body, so the old :srcdoc="body" was empty). (2) Markdown — the view now defaults to the rendered preview for all roles (showPreview = true), not just read-only users; editors were defaulting to the raw CodeMirror source. PNG rendering already worked and was left as-is. Reopened the defect to in-development with findings. vue-tsc clean; web bundle rebuilt. Requires a server restart to embed the new bundle.

  • 2026-06-13 — Fix the integration suite (go vet -tags=integration + go test -tags=integration), which had broken from concurrent test-developer runs. (1) Compile break — three helper redeclarations in package integration from different runs: renamed setupFakeClaudeWithOutputsetupFakeClaudeWithLines (agent_ws), setupFakeClaudeWithScriptsetupFakeClaudeWithRawScript (agent_metrics), seedAgentRunseedAgentRunRow (agents_api), keeping the other variant canonical in each case. (2) Stub didn't satisfy the truncated-stream contractsetupFakeClaude(t,0) emitted nothing, so under the (now-enforced) truncated-stream detection a clean exit with no type:result is correctly marked failed; the batch/lifecycle tests that expect a successful run were red. setupFakeClaude now emits the system/init (bypassPermissions) + type:result events a real successful run produces (shared fakeClaudeSuccessEvents); the two tests that deliberately want a zero-output run (TestSupervisor_NonClaudeRun_NoMetrics, TestAgentWSFinished_NoResultLine_ResultNull) use a new setupFakeClaudeSilent; the defect-writing stubs emit the events too. (3) Backfill test schema — the hand-rolled agent_runs table in backfill_metrics_test.go was missing every metric + model column, so the backfill UPDATE hit "no such column"; added them to match the real schema. (4) Backfill idempotency — reverted the cmd/kaos-control/backfill.go query to metrics_available=0 only (the earlier OR model IS NULL widening re-processed result-lines-without-modelUsage every run; the one-time re-stamp of already-backfilled rows is complete). go vet -tags=integration clean; all previously-failing non-docs integration tests green. Remaining red: the 5 TestDocs* tests belong to the in-development docs-panel-viewer feature (its QA cycle), not this fix.

  • 2026-06-13 — Backfill originating idea artifacts for features that shipped without one, for lifecycle traceability and release accounting. Created lifecycle/ideas/codex-cli-driver.md, gemini-cli-driver.md (covers both the gemini API driver and the gemini-cli/agy driver), and mobile-responsiveness.md — all status: done, release: KC-Release3, each linking to the relevant PROJECT_PLAN entries, source files, RELEASE_NOTES-0.1.3.md, and sibling lineages. mobile-responsiveness links to the existing mobile-responsiveness-followups idea. Two of the requested features already had done ideas: agent-usage-analytics-report (already KC-Release3, untouched) and claude-hooks-driver (the claude-mediated driver) — the latter was missing a release: field, so added KC-Release2 to match its requirement/defects (it shipped in the previous release, not Release3).

  • 2026-06-13 — Compute input vs output cost in the usage report (the per-model summary's Input/Output Cost columns were stuck at $0.00). The run data carries four token buckets (uncached input, cache-write, cache-read, output) plus a lump total_cost_usd, but no pre-split cost. New internal/reports/pricing.go adds a per-model-family list-price table — Sonnet $3/$15, Opus $5/$25, Haiku $1/$5, with cache-write/read rates — each verified to reproduce the recorded costUSD to the cent against real runs. splitCost() apportions the recorded total into input-side (uncached input + cache-write + cache-read) and output cost, then scales the two so they sum exactly to the recorded total, so the split always reconciles even if a list price drifts; unknown models yield no split rather than wrong numbers. agent_usage.go accumulates TotalInputCostUSD/TotalOutputCostUSD (overall + per-model + per-agent) — computed live from the already-stored token columns, no schema change or backfill. Frontend already binds these fields. Reconciliation + prefix-match unit tests in pricing_test.go. Validated on kaos-control: $572.87 total → $431.47 input / $141.40 output (cache reads dominate the input side). Caveat documented in code: the price table needs updating when Anthropic changes pricing — the reconciliation tests will catch drift.

  • 2026-06-13 — Capture the agent model for the usage report — it was always NULL, so every Reports chart grouped under "(unknown)". The design intended to read the actual model from each run's type:result line (UpdateAgentRunMetrics documents it), but ParseResultLine never extracted it, run-finish metrics omitted it, and the backfill didn't write it; run-start only stamped the requested model, which is empty whenever an agent uses the CLI default. Fix: internal/agent/result.go now derives the primary model from the result line's modelUsage map (the entry with the most output tokens — Claude Code runs a cheaper background model, e.g. haiku, alongside the primary), exposed as RunResult.Model; agent.go writes it into the run-finish metrics; cmd/kaos-control/backfill.go writes it too and its query widened to metrics_available=0 OR model IS NULL/'' so rows already backfilled (metrics-only) get re-stamped. New TestParseResultLine_Model / _ModelAbsent. Backfilled kaos-control's 658 runs: 466 sonnet-4-6, 116 opus-4-6, 26 haiku-4-5, 10 opus-4-7, 40 (null — logs without a parseable result line).

  • 2026-06-13 — Merge origin/main into kc-dev and complete the Vite 5 → 6 / Vitest 1 → 4 upgrade in tests/web. The only commit on main was a Dependabot bump of vitest to 4.1.0, which is incompatible with Vite 5 (vitest 4 imports vite/module-runner, absent in Vite 5) — it broke the whole frontend suite at startup. web/ was already on Vite 6 + vitest 4, so the fix was to bring tests/web in line: added an explicit vite ^6.4.2 (it had been pulling Vite 5 transitively), bumped @vitejs/plugin-vue ^5.0.4 → ^5.2.4 (peers vite ^5||^6), and vitest ^4.1.6. Then worked through Vitest 4's behavioural changes: (1) removed poolMatchGlobs/poolOptions from vitest.config.ts — both removed in v4; v4 defaults the pool to forks (one per file) so perf-file isolation is preserved automatically. (2) Fixed new ResizeObserver() mocks in the three 3D-graph test files — v4 forbids new on a vi.fn() with an arrow implementation, so switched to a regular function. (3) queueStore FS3 tests: v4's microtask flush now lets the post-event _silentRefresh() resolve within await nextTick(), re-applying the stale fetch mock and clobbering the optimistic WS-handler update — reject the refresh so the handler is asserted in isolation. (4) ArtifactRunHistory reactive-update test: same class — the onMounted fetchRunsByTargetPath() now resolved mid-test and overwrote the manual $patch; hang the fetch. (5) RoadmapView.periodMode M8.3: v4's vi.restoreAllMocks() no longer clears vi.fn() call counts (only spies), so getConfig's count accumulated across the file — added vi.clearAllMocks() to the describe's beforeEach. (6) Leaked-fetch unhandled rejections are now fatal in v4 — mocked @/stores/project in QueueView.test.ts (its onMounted calls fetchProjects()) and @/api/agents in artifact-blocked-questions.test.ts (the editor calls fetchAgents() on mount). None of these were app bugs — all were test fragilities exposed by stricter v4 timing/rejection handling. Frontend suite green at 1484/1484 (97 files), exit 0; web/ build + type-check unaffected (already on Vite 6).

  • 2026-06-13 — Group the project sidebar into four functional sections. The flat 15–16-item navItems list had outgrown a single column, so web/src/components/layout/AppSidebar.vue now builds a navSections: { title, items }[] structure rendered as Activity (Dashboard, Feed, Reports), Content (List, Board, Map, Roadmap, Testing, Documentation), Automation (Agents, Queue, Scheduler), and System (Config, Ollama, DevOps [role-gated], Parse Errors). Headers are static labels (chosen over collapsible groups for simplicity); when the sidebar is collapsed the text headers are hidden and a thin centred divider stands in for each group (the first group's leading divider is suppressed so nothing sits directly under the project header). Section titles fade with the existing collapse/expand opacity animation. Each entry remains a .nav-item with a .nav-link, so badges (Parse Errors count, Testing approved count) and active-route styling are unchanged. Tests: updated expectedLabels/allExpectedLabels to the new section order and the per-view nav-link count; added a functional section grouping describe asserting the four headers render in order and that headers carry no link and aren't .nav-items. Frontend 1484/1484 green; vue-tsc clean; make build-web succeeds.

  • 2026-06-12 — Fix three stale tests left behind by concurrent agent feature work (no product regressions — all three failures were tests lagging the code). (1) Backend internal/watcher/TestReleaseHandler_APIOriginated_NoWSEvent — the test registered the raw t.TempDir() path via ExpectedEvents.Expect, but ReleaseHandler.Handle resolves symlinks (EvalSymlinks) before Consume, and in production DiskSync.Write records the sandbox.Resolve-resolved path — so on macOS the temp dir's /var → /private/var symlink made the keys diverge and the API-originated write was not suppressed (spurious release.changed WS event). Linux CI passed because /tmp isn't symlinked. Fixed by resolving the path before Expect, mirroring production. (2) Frontend AppSidebar.test.ts — the sidebar gained two nav items from concurrent work, Reports (8461c390) and Documentation (5224cae0); the test's expectedLabels / allExpectedLabels arrays (13) and the per-view nav-link count assertion (hard-coded 13) were stale. Updated to the 15-item set in component order. (3) Frontend releases-api-unwrap.test.ts — the releases API client now maps file_path and slug onto every unwrapped Release (web/src/api/releases.ts); the test's sample fixture omitted them, so toEqual failed. Added both fields. Full backend -short suite green; frontend 1482/1482 green.

  • 2026-06-12 — Agent Usage Analytics Report backend (Milestones 1–7, plan lifecycle/backend-plans/agent-usage-analytics-report-3-be.md). M1: schema migration — ten new nullable columns on agent_runs (model, total_cost_usd, duration_api_ms, num_turns, input_tokens, cache_creation_tokens, cache_read_tokens, output_tokens, ttft_ms, metrics_available) via idempotent ALTER TABLE; two new covering indexes (idx_agent_runs_started_at, idx_agent_runs_agent_name); AgentRunRow extended with pointer fields and all scan/select helpers updated. M2: on run finish, parse the log via agent.ParseResultLine and write cost/token metrics plus metrics_available=1 via new index.UpdateAgentRunMetrics; stamp the requested model via index.SetAgentRunModel immediately after insert; new index.SetAgentRunTTFT for time-to-first-token persistence. M3: TTFT capture — isFirstContentToken helper detects the first {"type":"assistant"} event with a non-empty text block; OnTTFT func(ms int64) callback on Run struct wired by the Manager for streaming drivers. M4: kaos-control backfill agent-run-metrics --project <id> one-off command in cmd/kaos-control/backfill.go — queries metrics_available=0 terminal rows, parses each log, writes metrics; --dry-run flag; safe to re-run. M5: internal/reports/agent_usage.goBuildAgentUsageReport runs one SQL SELECT, streams rows into per-dimension accumulators, computes median/p95 via sort, fills zero-run buckets, returns AgentUsageReport with summary (overall/per_model/per_agent) and series/series_by_model/series_by_agent. M6: GET /api/p/:project/reports/agent-usage route registered under the existing project-scoped chi group; internal/http/reports.go handler parses from/to/agent/status/bucket/tz params and delegates to the aggregator. M7: documentation updated. go build ./... + go vet ./... pass across all milestones.

  • 2026-06-12 — Fix defect version-file-pre-release-suffix: removed the a suffix from VERSION so it reads 0.1.3 (bare semver matching ^[0-9]+\.[0-9]+\.[0-9]+$). TestVersionFile_ExistsAndIsValidSemver requires bare semver; pre-release labels belong in git tags (v0.1.3-alpha), not the VERSION file. go build ./... + go vet ./... pass.

  • 2026-06-11 — Auto-triage integration test suite (Milestones 1–9, plan lifecycle/test-plans/auto-triage-new-ideas-5-test.md). M1: tests/integration/triage_helpers_test.gotriageCfgYAML constant, installLLMFake/installLLMFakeError swapping ideachat.CallLLM, defaultProposeJSON, writeRawIdea, readArtifactFM, pollForArtifactStatus, pollForRunStatus, newTriageTestEnv/newTriageTestEnvWithSeeds. Production change: internal/ideachat/llm.go CallLLM converted from named function to package-level var pointing to callLLMImpl, enabling test injection without build tags. M2: internal/triage/eligibility_test.go — added TestEligible_WrongStatus_Clarifying and TestEligible_CaseSensitivity (capital-R Rawwrong_status). M3: internal/triage/run_test.go — 10 tests covering rewriteBody (fresh triage, re-run idempotency, no-H1, agent-H1 strip, title preservation), mergeAndFilterLabels (merge+dedup+vocab-filter), priority defaulting, and marshalArtifact round-trip. M4: internal/triage/triage_test.go — added TestTrigger_LockReleasedOnFailure verifying lock is released after execute failure. M5: tests/integration/triage_watcher_test.go — 6 watcher→triage tests (create raw→draft within 5s; draft/defect/modify-draft all produce no runs; rapid writes coalesce to one run; status-reset re-triage preserves ## Raw Idea block). M6: tests/integration/triage_startup_test.go — 4 startup re-scan tests (single raw, empty dir, three raw with cap=2 all triaged, pre-draft skipped). M7: tests/integration/triage_api_test.go — 8 REST endpoint tests (401/403 unauth, 403 wrong-role, 404 unknown slug, 409 wrong-status/wrong-type/locked, 202 success with run_id, in-flight coalesce same run_id). M8: tests/integration/triage_failure_test.go — 6 failure/observability tests (malformed JSON, clarify action, empty body, sandbox traversal path rejected at eligibility, no-retry after failure, slog captureHandler verifying triage failed record has path/lineage/reason fields). M9: lifecycle/tests/auto-triage-new-ideas-6-test.md companion artifact. All unit tests pass; integration build succeeds.

  • 2026-06-11 — Auto-triage frontend (Milestones 1–4, plan lifecycle/frontend-plans/auto-triage-new-ideas-4-fe.md). M1: added web/src/api/ideas.ts with triageIdea(project, slug) typed wrapper for POST /ideas/{slug}/triage; Vitest unit tests (success/409/401); vitest jsdom environment wired into vite.config.ts. M2: new TriageNowButton.vue component — visible only for type=idea/status=raw artifacts viewed by users with product-owner, analyst, or reviewer roles; uses artifact.lineage as slug; shows inline ApiError reason on failure; emits triage-started with the run_id. ArtifactEditorView.vue renders the button adjacent to QueueWorkButton; onTriageStarted handler immediately refreshes the artifact run history. M3: verification pass — WorkspaceView already routes AGENT_EVENTS (including agent.started) to agentsStore.onWsEvent; agentsStore auto-refreshes artifactRuns when event target_path matches; no new code needed. M4: updated raw status pill in StatusDropdown.vue to orange (#ffedd5/#c2410c light, #431407/#fb923c dark) to distinguish it from the near-identical grey used by draft; raw is already present in ArtifactListView status filter dropdown. All milestones: vue-tsc --noEmit + pnpm build + pnpm test (31/31) pass.

  • 2026-06-05 — Fix false truncated_stream failures introduced by the 2026-06-02 detection. A user-supplied run log from sol.packsin.com showed a qa (claude-code-cli) run that completed perfectly — read its test artifact, ran vitest, wrote two defect files, emitted {"type":"result","subtype":"success","is_error":false} and exited 0 — yet was reported FAILED. Root cause: the truncated-stream check at internal/agent/agent.go gates on resultEventSeen, which is only set inside the broadcast closure for events actually read off the progress channel. But runPrecheck/runMediatedPrecheck (internal/agent/precheck.go) return the instant they see the system/init event, and supervise had no drain loop on the precheck-pass path — so every post-init event, including the terminal result, sat unread in the buffered (cap 64) progress channel. resultEventSeen stayed false and every clean Claude run was downgraded done → failed. Two latent knock-ons from the same gap: runs emitting >64 events would fill the buffer, block the stdout reader, and stall to timeout; and post-init events never reached WebSocket subscribers (live agent view missing all tool calls after init). Fix: added a drain loop after the precheck switch that forwards remaining events through the existing broadcast closure (sets resultEventSeen, feeds the WS view, removes the stall); extracted a shared progressPayload helper now used by both precheck loops and the supervisor drain. New supervise-level regression tests (TestSupervise_ClaudeRunWithResultEventMarkedDone / …WithoutResultEventMarkedFailed) drive a fake process emitting init+result and assert done, while init-only still yields failed; verified the positive test fails on the unfixed code and passes after. Defect raised at lifecycle/defects/agent-run-false-truncated-stream-failure.md. The original detection's unit tests only exercised the predicate functions in isolation, so nothing drove supervise end-to-end and the regression slipped through. Full Go unit suite + go vet + go build ./... green.

  • 2026-06-02 — Detect truncated-stream Claude runs. Audited 32 user-supplied run logs and found two cases where the Claude binary exited cleanly with no terminal {"type":"result"} event — once after issuing a Read tool_use (10 s total), once after 5× 529 retries (30 s total). supervise() was marking these done despite the agent's task being incomplete — exactly the user's "didn't finish well with no errors" report. Fix in internal/agent/agent.go: track resultEventSeen via the broadcast closure (isResultEvent predicate); for drivers that emit the stream-json terminal contract (claude-code-cli, claude-mediated, gated by driverEmitsResultEvent), a clean exit without a result event now downgrades donefailed with failure_reason="truncated_stream". Reason flows through the existing agent.failed WS payload to the UI. Non-stream-json drivers (ollama, codex-cli, gemini, gemini-cli, shell-stub) are intentionally excluded — they don't share the contract. New TestDriverEmitsResultEvent (9 cases) and TestIsResultEvent (6 cases) lock the predicates. Full Go unit suite green.

  • 2026-06-02 — Two-part follow-up to the 529 fix. (A) Surface api_retry events in the UI. The Claude binary retries internally up to 10 times on transient API errors with exponential backoff — for the reported 529 the cumulative retry budget was ~218 s during which the run looked frozen. formatEvent in web/src/stores/agents.ts now recognises {type:"system", subtype:"api_retry"} and renders ↻ retrying after 529 (attempt 3/10, 2.3s backoff) lines into the progress log. New tests/web/agentsStore.apiRetry.test.ts covers the 529 / 429 / graceful-missing-max-retries cases. (B) Pick the right pause for overload vs. true rate-limit. Anthropic 529 typically clears in minutes, but the dispatcher's FallbackPause (30 min default) was tuned for quota/hourly rate-limits — too long for transient overload. Added OverloadPause (5 min default) and threaded a kind classifier through: extractRateLimitText now returns a RateLimitKind (rate_limit | overloaded), the supervisor's queue.rate_limit broadcast includes kind, the dispatcher's runResult carries rlKind, and handleRateLimit picks OverloadPause when kind=="overloaded" and the rawText has no parseable reset. New TestDispatcher_OverloadPauseUsedForOverloadedKind locks the path with distinctive 45 min / 7 min values to assert the right pause is selected. Frontend 1455/1455 (+3) green; full Go unit suite green.

  • 2026-06-02 — Recognise Anthropic 529 "Overloaded" + HTTP 429 in extractRateLimitText so the queue pauses-and-retries on transient server overload instead of marking the job a hard failure. User reported a real-world run where {"type":"result","subtype":"success","is_error":true,"result":"API Error: 529 {…overloaded_error…Overloaded}"} was treated as a generic failure — the dispatcher's pause/retry mechanism is exactly what 529s want (Anthropic recommends retrying after a backoff), so widening the matcher gets the right behaviour for free. Added overloaded and \b(429|529)\b patterns to the quotaExhaustedRE regex; three new test cases lock the 529, 429-positive, and 500-negative cases (HTTP 500 stays a hard failure, not a retry). All twelve TestExtractRateLimitText subtests green.

  • 2026-06-01 — Mobile responsiveness pass (M1–M6). Investigated baseline state — only ~12% of Vue files had any @media rules, the sidebar was a fixed 220 px column stealing 58% of a 375 px viewport, 11 data tables had no overflow handling, 15+ modals were centred desktop dialogs, and the 3D map view was effectively unusable on phones. Six commits land progressive coverage: (M1, c94c31ed) responsive foundation — --bp-mobile: 640px / --bp-tablet: 1024px tokens, useViewport() composable, global mobile helpers (touch-target floor, iOS-Safari font-size bump, .table-scroll utility); (M2, 929579f2) app shell — sidebar becomes an overlay drawer below 640 px, hamburger button in the header, ESC + route-change + backdrop tap dismiss; (M3, 4f3dfbf4) wrapped all 11 data tables in .table-scroll; (M4, d206f34f) modals go full-screen below 640 px via explicit class enumeration (the nine known overlay/panel pairs); (M5, 48db3dff) editor split stacks vertically, AgentsRuns and ArtifactList headers wrap; (M6, 96c44386) 3D map forced to 2D on mobile, 200 px filter rail becomes a slide-in panel with a toggle button. Follow-up items captured at lifecycle/ideas/mobile-responsiveness-followups.md (swipe gestures, cards-on-mobile for the tables, Kanban/Roadmap mobile design, polish items). Frontend suite 1452/1452 green at each milestone; type-check clean; production bundle builds.

  • 2026-05-30 — Merge origin/main into kc-dev to roll up Tim's codex-cli driver work (PR #9) plus the May 30 fixes (init-template kanban dedup, codex probe-timeout widening, gantt clippedRight off-by-one, e2e node_modules untrack + .gitignore repair). Resolved conflicts in .gitignore (keep .unison* from kc-dev plus .pnpm-store/ / **/__debug_bin* from main), internal/agent/agent.go (rebase the waitErr chan error field added on kc-dev onto the claudeProcesscliProcess rename from main; carry the field onto the renamed type), internal/agent/gemini_cli.go (rename in-file references to cliProcess), four web/src/components/agent/* files (add claude-mediated, gemini, gemini-cli to the radio set alongside the new codex-cli), and plans/PROJECT_PLAN.md (merge rolling-log entries chronologically). All Go unit + integration suites and the frontend suite green after the merge.

  • 2026-05-30 — Stop tracking tests/e2e/node_modules/ and repair .gitignore. The ignore rule (tests/e2e/node_modules/ on line 11) was added after pnpm artefacts had already been committed, so 338 files — .modules.yaml, the .bin/ shims, every package's LICENSE/README.md/*.d.ts/*.js/package.json — kept showing up in git status whenever pnpm rewrote them. git rm --cached -r tests/e2e/node_modules/ removes them from the index (791k lines of deletion); files stay on disk so the Playwright suite still runs. Also removed a stray ======= merge-conflict marker that was sitting on line 15 of .gitignore — git was silently treating it as an invalid pattern and the rules below it (.pnpm-store/, **/__debug_bin*) were being parsed inconsistently. Verified git check-ignore -v now resolves all four rules cleanly.

  • 2026-05-30 — Fix GanttChart's autoscale-mode clippedRight off-by-one that lit up M5.4: autoscale mode produces no clipped bars (bars always fit axis) when TODAY landed on May 30. With granularity=month, endOfGranularity(maxEnd, 'month') returns local-midnight of the last day (e.g. June 30 00:00). The bar check was addDays(e, 1) > rangeEnd, so a release ending on the last day computed July 1 00:00 > June 30 00:00 → wrongly clipped. The bug only surfaces when both addMonths(TODAY, ±1) land on the last day of their adjacent months (April 30 / June 30 on May 30; the test passed on most other days). Fix in web/src/components/releases/GanttChart.vue: introduce endOfDay(d) returning 23:59:59.999 of the local day, and change the check to clippedRight: e > endOfDay(rangeEnd). This is also timezone-robust — release dates parsed via new Date('YYYY-MM-DD') are UTC-midnight (= 10:00 in AEST), so the naive e > rangeEnd would still misfire in non-UTC zones; the end-of-day comparison absorbs that gap. Frontend suite back to 1452/1452 green.

  • 2026-05-30 — Stabilise TestCodexExecSupportsTimeout. The probe codexExecSupportsTimeout runs <binary> exec --help with a 2 s context deadline to sniff for the --timeout flag. Under concurrent test-suite load fork+exec of the shell shim missed that window, surfacing as --- FAIL: TestCodexExecSupportsTimeout (2.00s) expected timeout shim to be detected as supporting --timeout. Bumped the probe budget to 10 s — it's a one-shot driver-startup check, not perf-critical, and 10 s still bounds the worst case if the real codex binary genuinely hangs. Confirmed 3/3 green back-to-back agent-suite runs after the change.

  • 2026-05-30 — Port the duplicate-kanban: removal from kc-dev (bcb42052) onto main. The init template internal/initcmd/templates/config.yaml.tmpl had two kanban: mapping keys (lines 66 and 97) — the older, less-featured copy at line 97 was never deleted when the more complete columns/uncategorised/card_fields version was added at line 66. YAML rejects duplicate keys at the same level, so config.LoadProject on a freshly-initialised project failed with mapping key "kanban" already defined at line 66 and TestConfigTemplateLoadsCleanly was red. Deleted the second block; go test ./internal/initcmd/... green. (One intermittent flake remains in TestCodexExecSupportsTimeout — its 2-second probe deadline misses under concurrent load; consider lifting to 10 s in a follow-up.)

  • 2026-05-27 — Raise defect agent-config-form-missing-drivers against KC-Release3. web/src/components/agent/AgentConfigForm.vue only offers claude-code-cli, codex-cli, and ollama radio options, but the backend (internal/agent/agent.go:405-412) also registers claude-mediated, gemini, and gemini-cli. Users editing an agent whose YAML uses one of the missing drivers cannot preserve their selection on save. Assigned human/frontend-developer; the user will fix on kc-dev before release.

  • 2026-05-27 — Expose codex-cli in the agent UI. AgentConfigForm.vue now offers a Codex radio option and treats the model field as optional for Codex, while agent rows and run lists render a Codex label/badge. Run summary cards now hide Claude token-metric summaries for Codex runs, matching the current JSONL/raw-output driver contract. web/src/types/api.ts documents the new driver value. pnpm -C web run type-check passes.

  • 2026-05-27 — Add backend codex-cli agent-driver support. New internal/agent/codex_cli.go runs codex exec --json --dangerously-bypass-approvals-and-sandbox, passes --cd <project-root> for workspace correctness, optionally maps timeout_minutes to --timeout <seconds> only when the installed default Codex binary advertises that flag, and streams JSONL/raw stdout into existing progress/log plumbing. Shared CLI process handling was renamed from claudeProcess to cliProcess and now runs cmd.Wait() asynchronously so detached child processes cannot keep stdout/stderr pipes open forever. Added fake-binary driver tests, timeout-flag probe tests, detached-child regression coverage, and an opt-in live smoke (KAOS_TEST_LIVE_CODEX=1) against the installed Codex CLI. go test ./internal/agent ./internal/config and the live smoke pass.

  • 2026-05-22 — Override agy's --print-timeout so the agent isn't cut off mid-reply at the default 5-min wait. With --add-dir finally pointing agy at the project, run 87a65b0a1acf0647 actually did the work — ran the test suite and started writing the report — but agy aborted at exactly 5m6s with printmode.go:263 Print mode: timed out after 1495 polls (printed=41), which surfaces in our log as Error: timed out waiting for response. buildArgs now passes --print-timeout <TimeoutMinutes>m when the agent config sets a positive timeout, and --print-timeout 24h when it's 0 (kaos-control's "unlimited"), so agy's print-mode wait matches what the dispatcher already enforces. Three buildArgs subtests cover unlimited, explicit, and no-project-root cases; all green.

  • 2026-05-22 — Pass --add-dir <project_root> to the agy CLI so the agent actually sees the project files. Without it, agy's own log records workspaceDirs=[] and defaults to ~/.gemini/antigravity-cli/scratch regardless of the parent's CWD — the agent spends the entire 5-min --print-timeout budget hunting for the workspace ("I will list the parent directory…") and then exits with Error: timed out waiting for response. Confirmed via the user's own experiment (agy --dangerously-skip-permissions -p "what is the git status" from inside the project reported it was operating in /Users/keith/.gemini/antigravity-cli/scratch) and via the agy log line Creating CLI server backend: product=antigravity workspaceDirs=[]. buildArgs now inserts --add-dir <ProjectRoot> between --dangerously-skip-permissions and --prompt; the existing test split into withProjectRoot / withoutProjectRoot subtests, both green.

  • 2026-05-22 — Fix gemini-cli driver hang where agy exited but the agent stayed marked running until the user manually killed it. Symptom: DB showed run 512ea0e72bddf114 at 6m48s duration even though the log captured only an instant Error: timed out waiting for response from agy on stderr. Root cause: agy detaches an idle grandchild that inherits stdout/stderr FDs; the kaos-control pipe-drain goroutines blocked forever on Read() because the kernel never EOF'd the pipes (refcount stayed > 0 via the grandchild). supervise() drained the progress channel first and called proc.Wait() (→ cmd.Wait() which would have closed the parent-side pipes) only after — but the drain never exited because the goroutines never finished. Classic chicken-and-egg deadlock. Fix in internal/agent/gemini_cli.go: run cmd.Wait() asynchronously in a goroutine so it closes the parent-side pipes as soon as the agy process exits (regardless of grandchild FD holders), stash the wait result in a buffered waitErr chan error on claudeProcess (renamed to cliProcess post-merge with main), and have cliProcess.Wait() read from that channel when set (existing Claude-driver behaviour is unchanged because that field stays nil for them). New TestGeminiCliDriver_DetachedChildHoldsPipes reproduces the deadlock with a Python shim that fork()s a sleeping grandchild before exiting; mirrors supervise()'s drain-then-Wait ordering exactly. Verified: test fails (5s deadline exceeded) on the unfixed code and passes on the fixed code.

  • 2026-05-22 — Remove duplicate kanban: block from internal/initcmd/templates/config.yaml.tmpl. The template had two kanban: mapping keys (lines 66 and 97) — the older, less-featured copy at line 97 was never deleted when the more complete columns/uncategorised/card_fields version was added at line 66. YAML rejects duplicate keys at the same level, so config.LoadProject on a freshly-initialised project returned mapping key "kanban" already defined at line 66 and TestConfigTemplateLoadsCleanly failed. Deleted the second block; full suite green (make test-unit, make test-integration 182s, tests/web 1452/1452, make test-e2e 23/23).

  • 2026-05-22 — Fix claude-mediated regression where every tool call returned "malformed server response" (run fb1503454e5e6658). Root cause: commit 910582a9 correctly updated internal/http/permission_hook.go to emit Claude's canonical {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"…","permissionDecisionReason":"…"}} shape, but cmd/kaos-control/hookcmd/hook.go's response parser was still looking for the old top-level {"decision":"…","reason":"…"} envelope — json.Unmarshal succeeded against the new shape but serverResp.Decision == "", so every response fell into writeDeny("malformed server response"). The integration tests in tests/integration/hook_helper_test.go masked this because every stub was still emitting the old {"decision":"…"} shape. Fix: hook-helper now validates the canonical shape parses with a non-empty hookSpecificOutput.permissionDecision and passes the server's response bytes through verbatim (server already speaks Claude's protocol — the "translate" step is now unnecessary). All six test stubs updated to the canonical shape; all 8 TestHookHelper_* + 6 TestPermission_* cases green; end-to-end smoke via a local server confirms the canonical response is forwarded unchanged.

  • 2026-05-18 — GUI / CLI init parity, with auto-owner from the session. POST /api/projects/{project}/init and kaos-control init had diverged: GUI wrote an agentless config.yaml from yaml.Marshal(defaultProject()) with an outdated stage list (had dev-plans/sprints, missing docs), no seed files (CLAUDE.md, .claude/settings.json, .gitignore, devops/sample.yaml), and no users: entry — leaving the logged-in user with zero project roles, blocking every workflow transition via RolesFor(user.Email). Refactored internal/initcmd to expose a reusable ScaffoldProject(ScaffoldOptions) function that does the file/dir creation; CLI Run now delegates to it (keeping its flag parsing + auth-DB user creation as a wrapper). Rewrote handleInitProject to pull user.Email from userFromCtx and call the same ScaffoldProject — GUI now writes identical layout including the full template (10 agents/roles/stages) with the session user auto-populated in users:. Retired the now-unused public config.DefaultStages() / DefaultProjectConfigYAML() (private defaultStages/defaultRoles/defaultProject kept as in-memory fallbacks for LoadProject). Updated InitProjectModal.vue preview to list the actual scaffold (was missing prototypes/tests/docs/devops/seed-files, and still listed sprints). Updated tests/integration/projects_crud_test.go to assert landmark files exist + the session user's email appears in the rendered config.yaml; updated tests/cli_init_test.go to match the new stage list (drop sprints, add docs/lifecycle/devops/top-level devops, add devops/sample.yaml to the seed-file list). All four suites green; CLI + GUI scaffolds verified identical by smoke test.

  • 2026-05-18 — CLI polish: --version flag plus better unknown-flag handling. cmd/kaos-control/main.go gains a --version / -version / -V case in the top-level dispatcher that prints a three-line header (kaos-control <version> https://github.com/kcsinclair/kaos-control / Copyright / GNU AGPL v3.0). The usage banner now has a Flags section listing --version, --help, and -config. The default: clause previously let any leading-dash argument fall through to flag.Parse() (which printed only -config's help and exited 2 on unknown flags); it now catches unknown flags before the fall-through, prints the full friendly usage banner, and exits 1. The -config flag still falls through to run() so the implicit kaos-control -config /path serve form keeps working. Subcommand-level flag handling left untouched (user-confirmed out of scope).

  • 2026-05-18 — Fix startup-hang on schema-rebuild paths and harden the UX. Root cause: on a fresh/rebuilt index, IndexFile was calling git.Repo.FirstCommitDate for every artefact lacking a created: frontmatter — go-git's commitPathIter is O(commits) per file, so on a 700-file repo with ~3,300 commits startup took ~90 minutes. During that window the HTTP listener wasn't bound yet, so the UI looked hung; a user SIGTERM produced the misleading fatal: context deadline exceeded from the 10s httpSrv.Shutdown budget on a never-started server. Three changes: (1) internal/index/index.go drops the FirstCommitDate fallback in IndexFile — uses mtime as the single fallback when frontmatter has no created:. The git path was used to drive exactly one UI element (the artifact list's Created column); mtime is good enough and instant. (2) New kaos-control backfill-created subcommand (internal/backfillcmd/) walks lifecycle/**/*.md and inserts created: into the frontmatter using filesystem birth time (Darwin Stat_t.Birthtimespec; mtime fallback elsewhere). Atomic temp+rename writes; --dry-run and -v flags; skips files that already have created: and files without a frontmatter block. (3) cmd/kaos-control/main.go now binds the TCP listener before opening projects so the port is reachable from t=0; even if future startup work is slow the UI never appears hung. Measured: kaos-control's own 694-file project goes from 90 minutes to 18ms on warm-cache startup, ~76ms total to "kaos-control started".

  • 2026-05-18 — Sync kaos-control init template to canonical project config so newly-initialised projects ship the same lifecycle as kaos-control itself. internal/initcmd/templates/config.yaml.tmpl: added approver, devops, tech-writer roles (10 total); dropped sprints stage, added docs (10 total); added tech-writer, test-runner, docs-capture agent blocks ported from the canonical config (10 total); annotated allowed_write_paths on developer agents with "stack-specific — customise to your stack" inline comments per user direction. users: section is now always emitted (was conditional on -owner-email); with a supplied email it populates as before, without one it emits TODO@example.com + a 3-line comment explaining every project needs at least one user. scaffold.go drops lifecycle/sprints and creates lifecycle/docs. CLAUDE.md.tmpl directory tree updated to match. initcmd_test.go expectations bumped 7 → 10 for both agents and roles with comments naming each. Smoke-tested both with-email and without-email paths.

  • 2026-05-18 — Pre-release README pass plus a new frontend-lint-gap idea. README updates: status line bumped to "pre-1.0 (v0.1.x), working releases"; lifecycle-stage list now includes prototypes; agent list now names all four drivers (claude-code-cli, claude-mediated, ollama, shell-stub); tech-stack mentions the Ollama HTTP integration; Claude Code permissions section restructured into a two-driver intro with claude-code-cli-scoped setup steps and a "Detection at run-start" subsection replacing the stale "Coming in KC-Release1" callout; version pinned to 0.1.2 in install examples (was a mix of 0.1.3 / 0.1.1); PATH-instructions formatting fixed; first-run config example now shows the agent: block; Where things live table gains a per-agent-run-log entry. New idea lifecycle/ideas/frontend-lint-gap.md documents the JS/TS lint gap (no ESLint/Prettier; vue-tsc not in make lint) and proposes a two-stage fix (wire pnpm run type-check into make lint; add ESLint flat-config with a minimal correctness rule set).

  • 2026-05-16 — Repair the e2e Playwright suite (13 failing → 23/23 green) plus two real bug fixes uncovered along the way. (A) Reverted ArtifactEditorView.vue's auto-enter-edit-on-mount (commit 30a6e6a9) — it made the editor grab a lock on any navigation, including read-only views, and broke flows 03/06/08's read-mode assumptions. Reading an artifact no longer puts the user in edit mode. (B) Added useWebSocket(project, 'agent.started', …) to ArtifactListView.vue so the "Agent Running" pill actually appears mid-run; previously the list only refetched on artifact.indexed and agent.finished, so fast runs finished before the pill could appear. (C) Test-side patches: tests/e2e/harness/ws.ts accepts a cookieHeader and passes it via undici's headers extension (server closes unauthenticated WS with code 4401, so Node's cookieless WebSocket connections were timing out across flows 02/04/10); flows 02/04/05/10 updated to thread cookies through, fix one wrong-shape API response parse (data.run?.status not data.status), use getByRole('columnheader', { name: 'Runs' }) instead of a hasText regex that the SortHeader's icon-suffixed <th> text broke, and click the new modal-Edit button now that map node taps open ArtifactModal instead of navigating directly.

  • 2026-05-16 — Fix poor contrast on status / type pills in ReleaseDetailModal.vue. The modal was hard-coding light-theme pastels with a crude 3-bucket (terminal / active / default) classifier — every "intermediate" status (clarifying, planning, draft, approved, …) rendered the same washed-out light-grey pill with #94a3b8 text on #f1f5f9 (≈3:1 contrast, fails WCAG AA), and the type pills (IDEA / DEFECT) had no dark-mode story at all. Switched to the same data-status-driven palette already used by StatusDropdown.vue: one CSS rule per known workflow status (draft, clarifying, planning, in-development, in-qa, approved, done, blocked, rejected, abandoned, in-progress) with paired @media (prefers-color-scheme: dark) overrides. Type pills (idea / defect / default) gained dark-mode variants and the release's top-level Active / Planned / Shipped badge got the same treatment. Removed the now-unused artifactStatusBadgeClass helper. Note: palette is duplicated between this file and StatusDropdown.vue — worth a follow-up to extract to web/src/styles/status-badges.css if other components also need it.

  • 2026-05-16 — Recognise the terminal-result quota-exhausted message as a rate-limit so the job is re-enqueued instead of failing. extractRateLimitText previously only detected two stream-json shapes (error:"rate_limit" and error.type:"rate_limit_error"), but Claude can also exit a run cleanly with {"type":"result","is_error":true,"result":"You're out of extra usage · resets 11:10pm (Australia/Brisbane)"} — the model stopped, the binary exited 0, and the quota verdict was only visible in the wrap-up result line. Result: dispatcher treated it as agent.finished (or a generic failure), the job was marked completed/failed, the queue never paused, no re-enqueue. Added Format 3 detection: when the event is type:"result" with is_error:true and result text matches a small set of quota phrases (out of usage, usage … resets, rate.?limit, message limit, exceeded … quota/limit) we return the result text — which ParseResetTime's pattern 3 already parses for resets HH:MMpm (Area/City). Three new test cases in TestExtractRateLimitText lock the format-3 acceptance and the non-quota negative case.

  • 2026-05-15 — Fix queue dispatcher's cross-run event leak that caused spurious "lock conflict" failures. watchRunEvents was matching on event type only (agent.finished / agent.failed / queue.rate_limit) without checking the payload run_id, so any other concurrent run on the same project hub — a manually-started UI run, a previous queue iteration emitting a late event — would falsely signal that the current job had finished. The dispatcher would then mark the job completed and pick up the next one while the real underlying agent run was still alive and holding its lineage lock; the next job's StartRun would fail with lineage X is locked by …. Added a runIDCh channel: processNext hands the runID to the watcher after StartRun returns, the watcher drops terminal events with mismatched run_id, and drops them entirely until the runID arrives (since by definition they cannot be ours). New TestDispatcher_IgnoresForeignFinishedEvent injects a foreign agent.finished mid-run and asserts the dispatcher still completes both queued jobs in sequence.

  • 2026-05-15 — Backend plan artefact-relationship-labels-and-links-3-be.md Milestone 1: extracted EdgeKind* string constants (EdgeKindParent, EdgeKindDependsOn, EdgeKindBlocks, EdgeKindRelatedTo, EdgeKindMembers, EdgeKindWiki, EdgeKindAssigned, EdgeKindTimeline) into internal/artifact/artifact.go. Replaced all string literals in extractLinks() and internal/http/releases.go with the new constants. Milestone 2 verified: GET /api/p/:project/graph already returns source, target, kind on every GraphEdge and id (file path) on every GraphNode — no API changes required. go build ./... + go vet ./... pass.

  • 2026-05-15 — Fix claude-mediated hook response schema. The permission endpoint and the hook-helper fallback paths were emitting {"decision":"allow"}, but Claude Code's PreToolUse hook contract is {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"|"deny"|"ask","permissionDecisionReason":"…"}}. Without the hookSpecificOutput envelope Claude ignored the response and fell back to interactive permission prompts — visible in the user-facing run as our policy saying "allow" in permission_decision log entries while Claude's stream-json tool_result for the same tool_use_id returned "you haven't granted it yet". Updated internal/http/permission_hook.go to wrap responses via a new newHookResponse helper, and cmd/kaos-control/hookcmd/hook.go's writeResponse to emit the same envelope on every error path. Updated tests/integration/permission_endpoint_test.go to read from hookSpecificOutput.permissionDecision (plus assert hookEventName and permissionDecisionReason shape).

  • 2026-05-15 — Resolve absolute file paths against project root in policy.Evaluate. Claude Code sends absolute paths in PreToolUse tool_input (e.g. /Users/keith/Code/kaos-control/lifecycle/requirements/foo.md); the policy was just TrimLeft("/")ing them, leaving Users/keith/… which obviously never prefix-matches the project-relative AllowedPaths like lifecycle/requirements. Added ProjectRoot to PolicyConfig (populated from m.root); Evaluate now filepath.Rels absolute paths to project-relative before matching, and denies with rule=outside_project for absolute paths that escape the project root or when ProjectRoot is empty. Three new test cases lock the behaviour: inside-root resolves and matches, outside-root denies, and missing-root denies defensively.

  • 2026-05-15 — Fix the claude-mediated driver's hook config so Claude actually invokes the helper. The generated settings.json was using a flat PreToolUse: [{type, command}] shape, but Claude Code's schema requires a matcher wrapper: PreToolUse: [{matcher: "*", hooks: [{type, command}]}]. Without the wrapper Claude silently ignored the config and fell back to interactive-permission mode, which in headless -p runs surfaces as "you haven't granted it yet" and compound-Bash "requires approval" errors — looked like our policy denying things, but our policy was never called (no permission_decision lines in run logs). Added internal/agent/settings_test.go to lock the matcher-wrapped schema. Diagnosed from run 207bdbaab07e22e0 where three identical Write retries all returned Claude's own approval-needed text rather than any policy Rule field.

  • 2026-05-13 — Make OllamaDriver run logs match ClaudeCodeDriver's information density: (A) Header now includes # system_prompt: / # user_prompt: blocks (Claude logs the prompt via args=-p "<prompt>"; Ollama was missing it entirely). (B) New formatOllamaSummary helper captures the final done:true NDJSON event and emits a one-line # summary: done_reason=… total=… load=… prompt_eval=N (Tms, X.X tok/s) eval=N (Tms, X.X tok/s) tail before the # finished=… footer — durations rendered via time.Duration.Round(ms), tok/s derived from eval_count / eval_duration. Gracefully omits segments when stats are absent (older Ollama versions, generate vs chat differences). Two unit tests in internal/agent/ollama_test.go lock the format.

  • 2026-05-13 — Teach OllamaDriver to write its per-run log file. The driver was ignoring run.LogPath entirely, so Ollama runs streamed events to the UI over WebSocket but left nothing on disk — which broke the failure-banner "tail log" affordance and post-hoc debugging. Mirrored the ClaudeCodeDriver convention: open run.LogPath (creating parent dirs) before starting the request; write a header (# kaos-control agent run … # agent=… role=… driver=ollama instance=… model=… endpoint=… # started=<rfc3339>); tee every streamed NDJSON line to disk as it's emitted; record HTTP/scanner errors as # error: … breadcrumbs; emit # event: started / # event: completed markers plus the accumulated full-response text on success; always write a # finished=<rfc3339> footer via defer (so cancellation/error paths still close cleanly).

  • 2026-05-13 — Fix precheck_timeout failures for Ollama (and any non-Claude) agent runs. supervise() in internal/agent/agent.go was unconditionally calling runPrecheck, which waits for a system/init event carrying a permissionMode field — that event is part of Claude Code's stream-json protocol, so every Ollama run hit initEventTimeout and failed with observed_permission_mode="". Threaded Driver onto the Run struct (populated from ag.Driver); supervise() now branches: claude-code-cli keeps the existing precheck path; other drivers run a plain event-drain loop that forwards events to the hub until the process exits, sharing the same rate-limit re-broadcast closure. runPrecheck's signature is unchanged so existing unit tests keep passing. go build, go vet, tests/... integration suite all green.

  • 2026-05-13 — Fix integration-test regression in internal/config/config.go LoadApp: when a config YAML exists but omits projects_dir, cfg.ProjectsDir stayed empty and the unconditional os.MkdirAll(cfg.ProjectsDir, 0o700) introduced in 10b80f24 failed with mkdir : no such file or directory. Added a path-relative fallback (<config-dir>/projects) matching the one already in place for DataDir. Four TestOllamaConfig_* / TestOllamaRegression_* tests now pass; full integration suite green.

  • 2026-05-13 — Fix 4 frontend test failures surfaced after recent component churn. (A) tests/web/AppSidebar.test.ts "Milestone 4: badge preservation" — the three failing tests relied on mockResolvedValueOnce to satisfy fetchParseErrors, but two later additions on 2026-05-06 (GitStatusBar child mount → api.get('/git/status'), and testingStore.fetchApprovedCount) made parse-errors no longer the first api.get call (child onMounted fires before parent). Added a vi.mock('@/components/layout/GitStatusBar.vue', …) no-op stub alongside the existing module mocks. (B) tests/web/QueueView.projectNav.test.ts M5-3 — QueueView.onMounted now strips ?project=<unknown> via router.replace when the name doesn't match a known project, matching what the test explicitly documented as expected. (The QueueView.vue change itself was committed in parallel by an agent run, 683ec5be.) Plan: plans/fix-frontend-test-failures.md.

  • 2026-05-13 — Type drift fix in two queue test helpers after a61b882f renamed QueueJob.agentQueueJob.agent_name: tests/web/QueueView.test.ts (helper + one override site) and tests/web/QueueView.projectNav.test.ts (helper) now use agent_name. The templates read job.agent_name so the rendered Agent value was undefined; tests asserting toContain('requirements-analyst') failed. Full suite: 1358/1358 pass.

  • 2026-05-13 — Remove dead nonInitEvent helper from internal/agent/precheck_test.go. staticcheck U1000 flagged it via make lint; the test that originally needed it ended up constructing events inline, so the helper was unreferenced. Five-line deletion; make lint clean.

  • 2026-05-13 — Queue UI discoverability. (A) AppHeader.vue queue badge now renders whenever the user is authenticated, not only when there's pending work or the queue is paused; idle state (0 pending, not paused) uses a new muted --idle modifier so it doesn't look alarming, amber stays for pending, red stays for paused. (B) Added a permanent Queue entry to the project sidebar (AppSidebar.vue) pointing at the global /queue route, between Agents and Scheduler with the ListChecks icon. Test fixtures updated: AppHeaderQueueBadge.test.ts (two FH1 cases now assert the idle badge + --idle class), AppSidebar.test.ts (three label-array expectations bumped 12→13 to include Queue). Also fixed two stale QueueWorkButton.test.ts assertions from the earlier milestone-1 visibility rework — they expected the button to be absent for non-matching agents but the current code renders it disabled with a tooltip; updated to match.

  • 2026-05-12 — Release packaging: new scripts/package-release.sh bundles each cross-compiled binary in dist/ into a versioned zip (kaos-control-<VERSION>-<os>-<arch>.zip) containing kaos-control/{kaos-control[.exe], README.md, LICENSE, CONTRIBUTING.md} with the binary's 0755 mode preserved. Also writes dist/SHA256SUMS (auto-detects sha256sum vs shasum -a 256), pre-cleans stale zips so re-runs at a different VERSION don't leave behind old artefacts, and skips-with-warning on missing per-platform binaries. New make package target depends on release so make package is the one-line release-pipeline call. README gains a "Note on Claude Permissions" section explaining the one-time claude bypass-acceptance step that's required on every machine before agent runs work (the issue Ben hit on 0.1.0). .gitignore now excludes /support/ so user-log drops there stay out of the repo.

  • 2026-05-12 — Fix defect default-config-data-dir-incorrect: LoadApp in internal/config/config.go now sets DataDir to <config-dir>/data before calling SaveApp on first run, so the generated config.yaml contains a correct data_dir value. Added TestLoadAppDefaultDataDir to verify the persisted value survives a reload. go build ./... + go vet ./... pass.

  • 2026-05-12 — Fix defect default-config-port-should-be-8042: defaultApp() in internal/config/config.go now uses :8042 as the default listen address instead of :8080. One-line change; go build ./... + go vet ./... pass.

  • 2026-05-12 — Fix defect config-yaml-not-created-on-first-run: LoadApp in internal/config/config.go now writes the default config to disk (via SaveApp) when the file doesn't exist on first run; also sets a sensible projects_dir default (<config-dir>/projects) before persisting. New installs have a concrete ~/.kaos-control/config.yaml to inspect and edit without manual setup.

  • 2026-05-11 — Commit internal/index/index_test.go (8-case unit-test for index.Count covering status+type, status-only, type-only, CSV-type-OR, and the no-filter total). Was left uncommitted by an earlier agent run during the source_types work; supports the ready-count code paths.

  • 2026-05-11 — Switch the agent-panel badge link from &type=<source_type> to &stage=<stage> so the URL matches the natural per-role view (e.g. /artifacts?status=approved&stage=test-plans for test-developer instead of &type=plan-test). Added a sourceTypeToStage map in AgentPanelRow.vue covering all 12 standard types; falls back to no stage filter if a custom type is encountered.

  • 2026-05-11 — Third-pass fix for agent-panel-ready-count-not-role-specific, this time on the frontend. Two issues: (a) the ready-count badge dropped to 0 on every page refresh because the only caller of fetchReadyCounts was the WebSocket artifact.indexed handler in WorkspaceView — on a cold load no event has fired, so the store stays empty. Added an initial void store.fetchReadyCounts(project) to AgentsRunsView.onMounted. (b) Clicking the badge took the user to /artifacts?status=<active_status>&type=<source_type> — same wrong-column bug the count used to have. Fixed AgentPanelRow.handleBadgeClick to use status=approved so the linked list matches what the launch dialog and the badge count both reflect. All 1158 frontend tests still pass.

  • 2026-05-11 — Second-pass fix for agent-panel-ready-count-not-role-specific — the first fix (approved status) still left the badge under-counting for developer agents. The AgentLaunchModal for any plan-* developer agent also lists approved defect artifacts whose frontmatter.assignees[].role includes the agent's role. handleGetReadyCounts in internal/http/agents.go now mirrors that behaviour: for agents whose source_types contains any plan-* entry, it lists approved defects and adds those whose assignees match the agent's roles. New integration test TestReadyCounts_DeveloperIncludesAssignedDefects covers the four cases (assigned to this role, assigned to a different role, no assignees, wrong status).

  • 2026-05-11 — Fix agent-panel-ready-count-not-role-specific defect. The ready-count badge on the Agents screen was counting artifacts in each agent's active_status (e.g. in-development for test-developer) — but active_status is the during-run status the agent transitions the artifact INTO, not the picking-from status. The agent driver picks up artifacts in status approved, which is also what AgentLaunchModal shows when the user clicks an agent. Switched internal/http/agents.go:handleGetReadyCounts to count by Status: "approved" (named readyInputStatus), keeping the source_types filter intact. Three integration test files (ready_counts_test.go, agent_panel_status_test.go, agents_ready_counts_smoke_test.go) updated to seed approved artifacts and reflect the corrected semantic; agentPanelCfgYAML gained source_types on agent-with-model / agent-no-model so the badge counts can legitimately differ between the two agents the tests inspect.

  • 2026-05-11 — Three test-fallout fixes after the auth-role-checks-mutations Milestone 8 (post-bootstrap user-create requires product-owner) and the earlier WS-auth exemption from requireAuth: (1) tests/auth_middleware_test.go TestWebSocketAuth_Rejected now accepts 401 or 404 — with /ws exempt from requireAuth, an unknown project falls through to projectMiddleware and surfaces as 404. (2) TestBearerAuth_SkipsCsrf now inspects the response body's error code; a 403 with csrf_missing/csrf_invalid is the only failure mode, since Milestone 8 introduced a separate handler-level 403 (forbidden) for non-product-owner callers. (3) internal/http/agents.go:76 error code aligned to not_found (matches the convention everywhere else in the file and in artifacts.go); the M4 plan was updated alongside.

  • 2026-05-11 — Repair integration test suite after the global auth middleware (b2921c1) made every /api/* endpoint require auth. tests/integration/helpers_test.go now auto-logs in as admin@test.local in newTestEnvFull and exposes an env.logout() helper for tests that verify the 401 path. Bulk-converted ~30 http.Get(env.baseURL + …) call sites to env.doRequest("GET", …, nil) across 15 test files; stripped now-unused net/http imports. Updated tests/integration/priority_roundtrip_test.go so concurrent goroutine readers attach session cookies via http.NewRequest+AddCookie. Two real production bugs surfaced in the process: internal/http/auth.go csrfMiddleware now skips CSRF for unauthenticated requests so they fall through to requireAuth and get a clean 401 (was returning misleading 403 csrf_missing); internal/http/devops.go handleCreatePipeline was writing to <root>/devops/<slug>.yaml while the list handler read <root>/lifecycle/devops/ — switched POST to devopsDir(). All Go unit/integration, frontend vitest (1158), and lint pass.

  • 2026-05-10 — Update two stale assertions in tests/web/DashboardView.test.ts (TC2, TC4) to reflect the side-by-side dashboard refactor. velocity-chart and activity-feed are now special-cased into the dedicated section[aria-label="Velocity and activity"] row by SIDE_BY_SIDE_IDS in DashboardGrid.vue, so the panels and bottom-charts containers no longer hold them.

  • 2026-05-10 — Debounce kanban board's artifact.indexedrefresh() handler in web/src/views/project/KanbanBoardView.vue (500 ms). Bursts of indexed events (agent runs, fsnotify cascades) used to trigger one full re-pagination per event; collapsing them into a single refresh prevents redundant /api/p/.../artifacts traffic.

  • 2026-05-10 — Fix board-loading regression introduced by global auth middleware. requireAuth in internal/http/auth.go now exempts SPA shell paths (anything not under /api/) and WebSocket upgrades (/api/p/*/ws); without these exemptions deep-links returned raw 401 JSON instead of serving the SPA, and unauth WS connections looped on close-code 1006. internal/http/ws.go now closes unauth WS connections with code 4401 so the JS client (which checks for that exact code) stops reconnecting. Frontend kanban paginator (web/src/composables/useKanbanBoard.ts) was infinite-looping when the backend silently clamped its limit=5000 to 500: rewrote to match the backend cap (PAGE=500), advance offset by actual rows received, and break on a 0-row page. .gitleaksignore added to suppress one false-positive (RFC 6455 example WebSocket nonce in tests).

  • 2026-05-10 — Documented blocked as a valid status in CLAUDE.md (was already in KnownStatuses at internal/artifact/artifact.go:34 but missing from the doc, causing agents to file spurious "blocked is invalid" Open Questions).

  • 2026-05-06 — Tier 1 security review wired into make lint: added govulncheck, gosec, and gitleaks to the lint pipeline. Bumped Go via toolchain go1.25.10 directive in go.mod to clear 19 reachable stdlib CVEs (now 0). Hardened logout cookie hygiene in internal/http/auth.go (HttpOnly/Secure/SameSite on cleanup Set-Cookie). gosec exclusions documented in the Makefile header with justifications (G104/G124/G201-202/G204/G301-302/G304/G306/G703/G705).

  • 2026-05-10 — Backend plan rename-graph-to-map-3-be.md Milestone 1 verified: no Go source changes required. go build ./... and go vet ./... pass. All "Graph" references in internal/ are internal type names (GraphNode, GraphEdge, GraphData), method names (handleGraph, buildRoadmapGraph), and the Index.Graph() query method — none are user-facing strings. The GET /api/p/:project/graph endpoint continues to serve data unchanged; only the frontend route label is being renamed. No code changes needed.

  • 2026-05-09 — Backend plan dashboard-clickable-filters-3-be.md Milestone 2 verified: KnownStatuses in internal/artifact/artifact.go defines the canonical vocabulary (draft, clarifying, planning, in-development, in-qa, approved, rejected, abandoned, done, blocked). StatusDistribution excludes done/abandoned and only returns statuses stored in the DB — every returned value is a member of KnownStatuses for well-formed projects. No discrepancy found; no defect raised. No code changes needed.

  • 2026-05-09 — Backend plan dashboard-clickable-filters-3-be.md Milestone 1 verified: GET /api/p/:project/artifacts?status=<s> correctly filters by any status value. buildWhere in internal/index/index.go applies status = ? exact-match with an index-backed column (idx_artifacts_status). StatusDistribution returns raw DB status values, all of which are valid filter parameters. GET /artifacts with no params returns all artifacts (matches Lifecycle Total). No code changes needed.

  • 2026-05-09 — README rewritten with a proper "Getting started" section: prerequisites, build, first-user bootstrap (auth-less POST /api/admin/users), kaos-control init for project scaffolding, project registration via ~/.kaos-control/projects/*.yaml. Added a "Where things live" table covering app config, per-project config, devops logs, and artifact paths. Default port corrected to :8042 (was :8080).

  • 2026-05-09 — Dashboard widget contract fixes: SummaryCountsWidget read stats.total (backend returns total_tickets); StatusDistributionWidget read data.items (backend returns data.distribution); VelocityChartWidget read data.items (backend returns data.buckets). Three near-identical frontend/backend field-name mismatches; component tests passed because each test mocked the wrong shape that matched the widget. Fixed all three.

  • 2026-05-09 — Dashboard tracked-types made configurable: new dashboard.tracked_types field in lifecycle/config.yaml (default ["ticket"]). internal/index/index.go DashboardStats, StatusDistribution, and CompletionVelocity now take a types list via a new trackedTypesClause helper. internal/http/dashboard.go threads p.Cfg.Dashboard.TrackedTypes through. kaos-control's own bootstrap sets tracked_types: [requirement, idea, defect]. Fixes the dashboard returning all-zeros on projects whose work-item type isn't ticket.

  • 2026-05-06 — Backend plan test-artifact-management-3-be.md Milestone 5 confirmed: Kanban "Show Tests" default-off toggle is a frontend-only concern; GET /api/p/:project/artifacts returns test artifacts in normal results (no server-side exclusion) so the toggle works without a new API call. No backend code changes needed.

  • 2026-05-06 — Backend plan test-artifact-management-3-be.md Milestone 4 verified: agent.finished and agent.failed broadcast in supervise() (internal/agent/agent.go lines 595-605) already includes "target_path": row.TargetPath. Frontend event consumers receive the target path and can correlate completions to queued test artifacts. No code changes needed.

  • 2026-05-06 — Backend plan test-artifact-management-3-be.md Milestone 3 verified: supervise() in internal/agent/agent.go correctly orders: semaphore released → git commit → lock released (line 575) → UpdateAgentRun to terminal state (lines 578-587) → hub.Broadcast agent.finished/agent.failed (lines 595-605). By the time the WS event reaches the frontend, the DB row is terminal and the lock is free for the next serial run. No code changes needed.

  • 2026-05-06 — Backend plan test-artifact-management-3-be.md Milestone 2: added Count(filter Filter) (int, error) to internal/index/index.go (runs SELECT COUNT(*) with the same buildWhere logic, no pagination); added count_only=true query-param branch to handleListArtifacts in internal/http/artifacts.go — returns {"count": N} without fetching artifact rows, enabling lightweight badge counts. go build ./... + go vet ./... pass.

  • 2026-05-06 — Backend plan test-artifact-management-3-be.md Milestone 1 verified: CREATE INDEX idx_artifacts_type ON artifacts(type) exists in createSchema() at internal/index/index.go:1385; buildWhere already handles type = ? filter correctly. GET /api/p/:project/artifacts?type=test is index-backed. No code changes needed.

  • 2026-05-06 — Rename analyst agents to phase-first convention: analyst-requirementsrequirements-analyst, analyst-plannerplanning-analyst. Updated lifecycle/config.yaml (names + git identities), internal/workflow/workflow_test.go comments, CLAUDE.md agent listing, all three plan docs, and 14 lifecycle artifact body texts. make build, make lint, make test-unit all pass. Plan: lifecycle/backend-plans/rename-analyst-agents-3-be.md.

  • 2026-04-29 — Backend plan editor-live-refresh-on-disk-change-3-be.md Milestone 3 verified: GET /api/p/:project/artifacts/*path in internal/http/artifacts.go computes sha256.Sum256(raw) and returns file_sha in every response; SHA is deterministic for identical content and changes when the file changes. No code changes needed.

  • 2026-04-29 — Backend plan editor-live-refresh-on-disk-change-3-be.md Milestone 2 verified: 150 ms debounce in watcher.go resets timer on each fsnotify event and fires exactly one file.changed per coalesced burst; subsequent writes after a quiet period each produce a new event. No code changes needed.

  • 2026-04-29 — Backend plan editor-live-refresh-on-disk-change-3-be.md Milestone 1 verified: file.changed WS event already includes {"path": "<relPath>"} in internal/watcher/watcher.go; hub broadcasts unmodified; WS handler forwards raw bytes. No code changes needed.

  • 2026-04-28 — Backend plan analyst-agent-sees-draft-ideas-3-be.md verified: all three milestones confirmed no-code (existing API fully supports status=approved and type= combined filtering; frontmatter.assignees present in list responses; no agent-run input validation added per plan). go build ./... + go vet ./... pass with zero code changes.

  • 2026-04-28 — QA run for frontmatter-role-assignment: 7/8 tests PASS; 1 defect raised — PUT /artifacts accepts assignee with empty who field (returns 200 instead of 400). Defect: lifecycle/defects/frontmatter-role-assignment-7-defect.md, assigned to backend-developer.

  • 2026-04-24 — Initial commit: original idea captured.

  • 2026-04-24 — Requirements flow completed: Q&A rounds, detailed spec distilled, lifecycle directory structure established, CLAUDE.md created with commit conventions, first implementation-plan artifact (plans/create-claude-md.md) saved.

  • 2026-04-24 — Three development plans generated from the detailed requirements: backend (…-2-be.md), frontend (…-3-fe.md), test (…-4-test.md). Each phased into 6 milestones with cross-plan coordination noted.

  • 2026-04-24 — M1 + M2 implemented: repo scaffold (cmd/, internal/, web/, Makefile), internal/artifact parser, internal/index SQLite layer, internal/project container, full read-only HTTP API (/artifacts, /graph, /labels, /lineages, /parse-errors). Fixed chi wildcard routing (greedy wildcard requires inline dispatch for suffix-matched sub-routes). Acceptance verified: 7 lifecycle artifacts indexed, graph returns 7 nodes + 19 edges.

  • 2026-04-24 — M3 implemented: internal/sandbox (path traversal guard), internal/git (go-git wrapper: branch-per-lineage, AddAndCommit, Log, identity resolution), internal/hub (WebSocket broadcast), internal/watcher (fsnotify debouncer → incremental re-index + events), write API (POST /artifacts, PUT /artifacts/*, DELETE /artifacts/*, POST /artifacts/*/rename), WebSocket endpoint (GET /api/p/:project/ws), git history handler. Acceptance verified: create via API → file on disk + branch created + commit; rename → inbound links rewritten in one atomic commit; external file drop → re-indexed in < 500 ms.

  • 2026-04-24 — FE-M1 implemented: Vite 5 + Vue 3.5 + TypeScript + Pinia + Vue Router scaffold under web/; typed fetch API client with CSRF double-submit; auth store (login/logout/fetchMe); project store; toast/ui store; Vue Router with auth guard (nav-guard calls /api/auth/me on first load); LoginView + LoginForm; ProjectPickerView (lists projects, shows user roles as chips); WorkspaceView shell (AppHeader, AppSidebar, RouterView); ArtifactListView placeholder; dark-sidebar layout with design tokens; pnpm buildweb/dist/ → embedded by Go binary. .gitignore updated: /dist/ roots the Go binary ignore, web/node_modules/ excluded.

  • 2026-04-24 — M5 implemented: internal/lock (lineage lock manager with SQLite persistence, heartbeat, reaper goroutine), internal/agent (Driver interface, ClaudeCodeDriver spawning claude --dangerously-skip-permissions -p, ring-buffer stderr, Manager with global semaphore + per-lineage locking, supervisor goroutine for exit/commit/broadcast, crash recovery), internal/http/agents.go (GET /agents, POST /agents/:name/run, GET/POST /agents/runs/…), git ModifiedFiles for scope-enforced post-run commit, index CRUD for agent_runs and lineage_locks tables, lock reaper wired into startup. lifecycle/config.yaml extended with backend-planner agent config.

  • 2026-04-24 — FE-M4 implemented: backend file_sha (SHA256 of raw file added to GET artifact response), lock HTTP API (GET/POST /locks, DELETE/POST /locks/:lineage/heartbeat) wired to existing lock.Manager. Frontend: api/locks.ts, stores/locks.ts (WS event-driven map), composables/useLock.ts (acquire on enter-edit, 30s heartbeat interval, release on cancel/save/unmount, 503 treated as lock-free), composables/useExternalChange.ts (file.changed WS + 3s save-grace window), MarkdownEditor.vue (CodeMirror 6 with basicSetup + markdown + oneDark theme + Cmd+S keymap), FrontmatterEditor.vue (typed inputs for title/status/labels/release/sprint/depends_on/blocks), LockBanner.vue. ArtifactEditorView.vue rewritten: read mode (existing preview) ↔ edit mode (CodeMirror | MarkdownPreview split + FrontmatterEditor), optimistic PUT with expected_sha, conflict error messaging, lock banner for held locks, external-change reload-or-keep prompt. TypeScript clean, 610-module Vite build clean.

  • 2026-04-25 — Agent runtime hardening: (1) wired AgentConfig.Model through Run.Model to the driver as --model <name>; analyst agents default to opus, developers + qa to sonnet; (2) per-agent timeout_minutes (default 0 = no timeout); supervisor distinguishes errors.Is(ctx.Err(), context.DeadlineExceeded) → status killed-timeout from context.Canceledkilled; (3) added blocked to KnownStatuses and to the workflow transition matrix (* → blocked for any agent role; blocked → draft for product-owner / analyst); every agent's prompt template gained a uniform "if stuck, append Open Questions and mark blocked" stanza; allowed_write_paths extended on each agent so it can write the block back to its input artifact; (4) driver switched to --output-format stream-json --verbose; stdout JSON events parsed by formatEvent() in stores/agents.ts into readable progress lines (assistant text, ▸ ToolName path, results); (5) per-run log file at <data_dir>/<project>/runs/<run_id>.log; new GET /api/p/:project/agents/runs/:run_id/log endpoint streams it; AgentsRunsView gained "View full log" button. UI status chip adds amber for killed-timeout. Spec §4.2, §6.2, §7 updated.

  • 2026-04-25 — Graph label filters: Labels []string added to GraphNode (populated via labels_index join); labels added to GraphFilter; uniqueLabels computed + OR-filter in stores/graph.ts; Labels chip group in GraphFilters.vue. Plan: plans/graph-label-priority-filters.md.

  • 2026-04-27 — Product-owner workflow bypass: workflow.HasProductOwner() exported helper; CanTransition short-circuits to true and AllowedTargets returns the full target set when the user has product-owner; transition.go skips the required-plans GateReady check for product-owner. Resolves defect product-owner-cannot-transition.md.

  • 2026-04-26 — Active node pulse visualization: ACTIVE_STATUS_COLORS added to graphConstants (in-development=green, in-qa=amber); 3D graph adds a semi-transparent pulsing torus ring via onEngineTick (sine wave scale 0.85–1.15 at 500 ms period); 2D graph pulses border-width between 2 and 6 px via setInterval at 700 ms. Plan: plans/active-node-visualization.md.

  • 2026-04-26 — Agent status lifecycle: active_status + done_on_success fields added to AgentConfig; PatchFrontmatterField moved to internal/artifact (was private in http/transition); agent manager sets target artifact status on run start and bundles done status into the agent's own commit on success; developer agents configured active_status: in-development, done_on_success: true; QA configured active_status: in-qa.

  • 2026-04-25 — Markdown editor line wrap toggle: Compartment from @codemirror/state used for dynamic EditorView.lineWrapping reconfiguration without recreating the editor; wrapLines ref persisted to localStorage (kaos-editor-wrap); "Wrap" chip button in toolbar above editor; defaults to on.

  • 2026-04-25 — Graph polish + artifact list improvements: priority ring rendered grey (#6b7280) when artifact status is done in both 3D (ForceGraph3D) and 2D (Graph2DView); 3D label nodes now render a canvas-texture THREE.Sprite text label above the sphere; 2D label nodes styled as pill (round-rectangle, auto-width); ArtifactListView badge styles migrated to CSS variables; priority dropdown added to artifact list filter bar.

  • 2026-04-25 — Light/dark mode toggle: stores/theme.ts (Theme type, applyTheme, useThemeStore with localStorage key kaos-theme); FOUC-prevention inline script in index.html; App.vue calls theme.init() on mount; sun/moon icon toggle button in AppHeader; tokens.css extended with [data-theme="dark"] block + @media (prefers-color-scheme: dark) fallback; fixed dark mode surface/muted contrast; added --badge-* CSS variables for all status variants.

  • 2026-04-25 — Artifact type vocabulary: renamed ticketrequirement throughout (artifact.go, config defaults, lifecycle/config.yaml, graphConstants.ts, ArtifactListView typeOptions); removed unused types epic, plan-dev, release, sprint; restored test type pointing to lifecycle/tests/; ARTIFACT-TYPES.md reference doc added to repo root; lifecycle/prototypes/Kaos Control/ prototype HTML committed.

  • 2026-04-25 — Priority frontmatter field: Priority string added to Frontmatter; schemaVersion bumped to 2 (auto-rebuild); priority column + index added to artifacts table; Priority wired through Filter/buildWhere/GraphNode/Graph SELECT/upsert; new Priorities() method + GET /priorities endpoint; priority server-side filter param on artifact list; graph chips + artifact list dropdown (dynamic, hidden when empty). Plan: plans/graph-label-priority-filters.md.

  • 2026-04-25 — Index path-escape fix: IndexFile and the watcher both compute filepath.Rel against the symlink-resolved project root (matching the earlier sandbox fix); they refuse to index files whose computed relative path begins with .. or is absolute. Added pruneEscapingPaths() at startup which deletes existing rows from artifacts, parse_errors, links, and labels_index whose paths begin with .., /, or contain /../ — fixes stale entries from past firmlink/Rel mismatches.

  • 2026-04-25 — Documentation refresh: rewrote CLAUDE.md to reflect actual state (was still describing the pre-code phase) — added repository layout tree, build/run commands, tech-stack-in-use, role/agent overview, indexing behaviour, and frontmatter requirements. Added a short README.md as a quick-start placeholder; comprehensive docs deferred.

  • 2026-04-25 — Role vocabulary migration: renamed backend-plannerbackend-developer, frontend-plannerfrontend-developer, developertest-developer; added analyst role; added defect artifact type + lifecycle/defects/ stage. Six agent configs in lifecycle/config.yaml: requirements-analyst, planning-analyst, backend-developer, frontend-developer, test-developer, qa — each with focused prompt template and scoped allowed_write_paths. internal/config/config.go defaults updated; internal/workflow/workflow.go transition matrix updated (analyst can self-submit, three developer roles authorised for in-development→in-qa); internal/artifact/artifact.go KnownTypes and stageToType() extended for defects. required_plans.ticket set to [plan-backend, plan-frontend, plan-test] (gates planning→in-development). Spec (Innovation Maker - Making Releases from Ideas-1.md) updated in same commit: §2 personas, §4.2 type vocabulary, §5.1 directory layout, §6.2 transition matrix, §6.3 plan gating, §7.1 agent example, §13.3 example config. tests/.gitkeep added at repo root for test-developer's write target.

  • 2026-04-25 — FE-M6 implemented: ParseErrorsView.vue (table of path + message, Reload button, success state), ProjectConfigView.vue (YAML textarea editor, unsaved-changes indicator, Save), Graph2DView.vue (Cytoscape + cytoscape-fcose, lazy-loaded via defineAsyncComponent), 3D/2D toggle in GraphView.vue, parse-error count badge in AppSidebar.vue (WS-driven refresh on artifact.indexed), GET/PUT /api/p/:project/config backend endpoint. Bundle: main 45 KB gzip; vendor-three 359 KB, vendor-codemirror 208 KB, vendor-cytoscape 176 KB — all loaded only when feature is used. TypeScript clean, 638-module Vite build clean.

  • 2026-04-24 — FE-M5 implemented: TransitionDialog.vue (fixed status list, comment textarea for rejections, calls POST /transition, emits transitioned event), RunAgentDialog.vue (agent chip selector, role select, target path input, calls agentsStore.startRun), AgentsRunsView.vue (table of runs with live status, expandable rows showing progress lines / stderr tail / artifacts produced, Kill button for running runs), RunStatusChip.vue (Teleported fixed-position pulsing chip for in-flight runs, navigates to agents view). WorkspaceView.vue rewritten to multiplex WS events to agentsStore (agent.) and locksStore (lock.). ArtifactModal.vue and ArtifactEditorView.vue extended with Change Status + Run Agent toolbar buttons. api/agents.ts + stores/agents.ts (runs list, progressLines map, WS event handlers, kill action). TypeScript clean, 624-module Vite build clean.

  • 2026-04-24 — FE-M3 implemented: GraphView (3D force graph with dark canvas, orbit/zoom controls), ForceGraph3D.vue (wraps 3d-force-graph; node colour by type, size by lineage index, directed arrowheads, HTML tooltips, ResizeObserver fill, _destructor on unmount), GraphFilters.vue (chip-based multi-select for type/status/lineage, live filtered node/edge count), GraphLegend.vue (overlay matching token colours), ArtifactModal.vue (Teleported overlay, fetches artifact detail from store, markdown preview, inbound/outbound edge list, Edit action navigates to editor), useGraphData.ts composable (fetches on mount, re-fetches on artifact.indexed WS event), stores/graph.ts (rawNodes/rawEdges, computed filteredNodes/filteredEdges from reactive filter, uniqueTypes/statuses/lineages), api/graph.ts. Router default /p/:project now goes to graph. GraphView chunk ≈ 1.3 MB gzip 363 KB due to three.js — lazy-load deferred to M6 per plan.

  • 2026-04-24 — FE-M2 implemented: ArtifactListView (server-side filter bar for stage/status/type/label, paginated table, WebSocket artifact.indexed invalidation), ArtifactEditorView (breadcrumb nav, markdown preview, frontmatter panel, WS live-reload), artifact components (LineageBreadcrumb, FrontmatterPanel, MarkdownPreview with markdown-it + wiki-link inline rule → /p/:project/artifacts?lineage=…), useWebSocket composable, stores/artifacts (items/filter/detailCache/labels), api/artifacts.ts, api/ws.ts (WsClient with exponential backoff reconnect, singleton per project). Router extended with artifacts/:pathMatch(.*)+ editor route. TypeScript clean (vue-tsc --noEmit), Vite build clean (157 modules).


Completed

  • Original idea captured: [lifecycle/ideas/Innovation Maker - Making Releases from Ideas.md](../lifecycle/ideas/Innovation Maker - Making Releases from Ideas.md)
  • Clarifying Q&A (two rounds): [lifecycle/ideas/Innovation Maker - Making Releases from Ideas-questions.md](../lifecycle/ideas/Innovation Maker - Making Releases from Ideas-questions.md)
  • Detailed requirements spec: [lifecycle/requirements/Innovation Maker - Making Releases from Ideas-1.md](../lifecycle/requirements/Innovation Maker - Making Releases from Ideas-1.md)
  • Repo guidance for Claude Code: CLAUDE.md (includes commit conventions)
  • Plan: Create CLAUDE.md — plans/create-claude-md.md
  • Backend development plan — [lifecycle/backend-plans/Innovation Maker - Making Releases from Ideas-2-be.md](../lifecycle/backend-plans/Innovation Maker - Making Releases from Ideas-2-be.md)
  • Frontend development plan — [lifecycle/frontend-plans/Innovation Maker - Making Releases from Ideas-3-fe.md](../lifecycle/frontend-plans/Innovation Maker - Making Releases from Ideas-3-fe.md)
  • Test plan — [lifecycle/test-plans/Innovation Maker - Making Releases from Ideas-4-test.md](../lifecycle/test-plans/Innovation Maker - Making Releases from Ideas-4-test.md)
  • M1 (skeleton): cmd/kaos-control/main.go, Makefile, web/embed.go, config loading, project registry, signal handling
  • M2 (artifact indexing): internal/artifact (parser, links, types), internal/index (SQLite schema, scan, all queries), internal/project (runtime container), HTTP API (/artifacts, /graph, /labels, /lineages, /parse-errors)
  • M3 (write path + git): internal/sandbox, internal/git, internal/hub, internal/watcher; write API + WebSocket endpoint; git history; watcher-driven re-index
  • M4 (auth + workflow): internal/auth (argon2id, session store), internal/workflow (state machine, GateReady); login/logout/me/create-user endpoints; CSRF double-submit; session middleware; POST /transition with role-matrix enforcement; rejection child artifact creation; lifecycle/config.yaml user binding
  • M5 (agent runner): internal/lock (lineage lock manager), internal/agent (Driver interface, ClaudeCodeDriver, Manager with semaphore + supervisor goroutine, crash recovery), agent HTTP API (list agents, start run, list/get runs, kill), git.ModifiedFiles, index CRUD for agent_runs + lineage_locks, lock reaper, agent config in lifecycle/config.yaml
  • FE-M1 (scaffold): web/ Vite + Vue 3 + TS + Pinia + Router; API client with CSRF; auth/project/ui stores; LoginView, ProjectPickerView, WorkspaceView shell with sidebar; design tokens; pnpm build → embedded by Go
  • FE-M2 (artifact list + read-only editor): ArtifactListView (filter bar, paginated table, WS invalidation), ArtifactEditorView (markdown preview, frontmatter panel, WS live-reload), LineageBreadcrumb, FrontmatterPanel, MarkdownPreview (markdown-it + wiki-link rule), useWebSocket composable, artifacts Pinia store + API layer, WsClient singleton with reconnect
  • FE-M3 (3D graph + modal): GraphView, ForceGraph3D.vue (3d-force-graph wrapper), GraphFilters.vue, GraphLegend.vue, ArtifactModal.vue, useGraphData.ts, stores/graph.ts, api/graph.ts; workspace default navigates to graph
  • FE-M4 (write path): backend lock HTTP API + file_sha in GET response; MarkdownEditor.vue (CodeMirror 6), FrontmatterEditor.vue, LockBanner.vue, api/locks.ts, stores/locks.ts, composables/useLock.ts, composables/useExternalChange.ts; ArtifactEditorView full read/edit toggle with optimistic PUT, lock lifecycle, external-change prompt
  • FE-M5 (workflow + agents): TransitionDialog.vue, RunAgentDialog.vue, AgentsRunsView.vue, RunStatusChip.vue; api/agents.ts, stores/agents.ts; WS multiplex for agent./lock. events; Change Status + Run Agent wired into modal and editor toolbar
  • FE-M6 (config + graph 2D + polish): ParseErrorsView.vue, ProjectConfigView.vue, Graph2DView.vue (Cytoscape + fcose, lazy-loaded), 3D/2D graph toggle, parse-error badge in sidebar, backend config GET/PUT endpoint, manualChunks bundle split

Planned

Next: Post-M6 — E2E Testing + Hardening

  • Playwright or Vitest browser-mode smoke tests for core flows
  • Error boundary components for async route failures
  • Skeleton loading states for artifact list and editor
  • POST /api/open-in-editor for local editor launch (spec §16)
  • POST /api/p/:project/agents/:name/preview-prompt (spec §16)

Roadmap: Remaining Frontend Milestones

  • Vue 3 SPA with Vite build pipeline
  • Artifact list + graph visualisation (3d-force-graph / Cytoscape.js)
  • Artifact detail view with markdown rendering (markdown-it)
  • Login / session management UI
  • Transition controls + agent run trigger UI
  • WebSocket integration for live updates
  • Acceptance: browser shows graph of all indexed artifacts; transition + agent run work end-to-end; auth gates protected views

Open Questions (parked)

Carry-overs from §17 of the spec — decide during implementation.

  • Finalise product name: kaos-control vs Innovation Maker.
  • Styling system: Tailwind vs small custom CSS layer.
  • Agent prompt template storage location.
  • SQLite schema migration strategy for index rebuilds across app versions.
  • Auto-collection cadence for labels/types (realtime fsnotify vs manual re-index).

References

  • Authoritative spec: [lifecycle/requirements/Innovation Maker - Making Releases from Ideas-1.md](../lifecycle/requirements/Innovation Maker - Making Releases from Ideas-1.md)
  • Workflow log & prompt library: project-notes.md
  • Agent/Claude guidance: CLAUDE.md