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.
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,~/.claudecredentials, git identity, and~/.kaos-control— and the whole path is rootless (binary in~/.local/bin, unit in~/.config/systemd/user, andloginctl enable-lingerfor self is permitted by systemd's default polkit, so no sudo anywhere). Design notes:ExecStartinvokes theservesubcommand rather than-d— equivalent on current builds, but pre-0.2.0 binaries reject-dwithunknown flagand exit 1 (caught by smoke-testing against the installed v0.1.2);-configis pinned explicitly because the user manager doesn't reliably inheritXDG_CONFIG_HOME;Environment=PATHspells out the per-user tool dirs since agent runs shell out toclaude/git/go/nodeand the user manager's default PATH is minimal (an optional~/.config/kaos-control/service.envoverrides it — needed here, as nvm-managedpnpmwas invisible to the service and would have brokenmake build-webpipeline steps);TimeoutStopSec=30covers the server's 10s graceful drain, after which the control-group kill reaps agent subprocesses. No hardening directives (ProtectSystemet al.) — agents legitimately write outside$HOMEinto project paths. Verified withsystemd-analyze --user verifyand smoke-tested end to end (enable/start/restart/stop, port release,/api/health— note/healthalone 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/crypto0.51.0→0.52.0 to clear 13 Dependabot alerts (7 critical) — allx/crypto/sshadvisories (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 ofx/crypto/argon2; limited real surface (not an SSH server) but critical-rated + trivial. Applied on bothmain(closes the alerts — Dependabot tracks the default branch) andkc-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 lintfully green. staticcheck: removed deadartifactRow/parseArtifactList/extractJSONFieldindevopscmd/client.go(lint-devops-cli-unused-json-helpers); removed the redundantif last != nilchecks ininternal/devops/logger.gobackfillRecord—first/lastare set together so the existingfirst==nilguard already ensureslast!=nil, which is what made SA5011 flag the deref (lint-logger-nil-deref-sa5011); removed unusedseedRunRecord/writeMinimalLogFile+ the now-unusedencoding/jsonimport inrun_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 raisedagent-runner-strip-schedulewakeup(medium): the one-shot agent runner should reject/stripScheduleWakeupstructurally rather than trusting each agent's prompt — the fixed test-runner still attempted it twice. -
2026-07-08 — Fix the
test-runneragent (defecttest-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 andScheduleWakeup(which the one-shot agent runner never honours), and bumpedtimeout_minutes30→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 (handleUpdateConfigwrites without refreshing the cachedp.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(devopstest-allpipeline) as intended, then triaged. Key finding: the test-runner agent is broken for long suites — it runs unit + vitest, then backgrounds integration/e2e and callsScheduleWakeupto resume, but kaos-control's one-shot agent runner never re-invokes it, so it parks (doneafter ~180s) and files 0 defects for the slow suites. Raised high-priority defecttest-runner-parks-on-schedulewakeup(suggested fix: run suites synchronously in-foreground, dropScheduleWakeup). Ran the abandoned suites manually: all green — Go unit 20/20, web vitest 1536/1536 (the agents cleared the earlier 20 frontend failures), Go integrationok(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, dupidea-archiving-6) —watcher.shouldProcessonly checked the filename's leading dot, so files in a dot-dir created at runtime (.trash/dot.md) were indexed even thoughaddDirRecursiveskipped watching the dir; now rejects any dot-prefixed directory ancestor (TestDotDirExclusion_RuntimeCreationpasses). (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 theStdoutPipe; a fast-exiting process lost its terminalresultline → falsetruncated_stream. The reaper (startCommandProcess) now drains the readers beforecmd.Wait(), bounded byreaderDrainGrace(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 storedbody_sha256matched the file — treating "content unchanged" as "row current". When the index row'sstatusdrifts from the file (observed onlifecycle/tests/idea-archiving-5-test.md: disk+gitapproved, index stuckdraft, hash matching the approved file), the guard locked the stale status in permanently — every re-index (startup scan, watcher, and the transition's ownIndexFile) skipped, while thestatus_transitionevent still fired (illusion of saving). Confirmed reproducible across a restart. Fix: the guard now also requires the storedstatusto match the parsed status before skipping, so the next index (transition / watcher / startup scan) self-heals the row. Regression testTestIndexFile_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 toclaude-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-unusedbase_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
projectfield (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) Fixedproject_queue_test.go(the botched M1 verification): it enqueued for unregisteredproject-a/project-b; M1 only needs "each job carries a non-empty project", so repointed both tests at the registeredtestprojectusing two distinct artifacts (FR3 duplicate-active suppression rejects a 2nd job for the same project+artifact). All 3TestProjectQueue_*pass; full integration suite green. (b) Raised defect queue-events-missing-broadcasts:queue.addedisn't broadcast on the normal enqueue path andqueue.cancelledis never broadcast, so other clients don't update live — the real blocker behind the plan's Open Questions, orthogonal to project-scoping. Fix = twobroadcastJobEventcalls (payload already carriesproject), which also makes the M1 client-side filter fully real-time. -
2026-07-05 — Unbreak the
tests/integrationbuild after a batch of agent runs left it red (same pattern as 2026-06-29). (1)go.mod— an agent (runa1244edee) committed a testify-using test (project_queue_test.go) but left the requiredgithub.com/stretchr/testifyrequire uncommitted; committed it (matchesgo mod tidy, go.sum already consistent). (2)open_questions_config_test.go(rund9e44e60) 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-questions→answer_format,internal/configOpenQuestionsConfig— was actually implemented). Outstanding:project_queue_test.gocompiles but fails at runtime (enqueues for unregisteredproject-a/project-b; the queue env only registerstestproject) — 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.HasOpenQuestionsalready treated a truly empty## Open Questionssection 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\nNonewrongly auto-blocked the artefact. Added 3 unit tests; the 7 existingHasOpenQuestionstests and theinternal/indexautoblock 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.maptraversal, 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; commit2f2d4161), then mergedmain → kc-dev.vite6.4.2→6.4.3 (dev) clears #31 (High) + #32 — both Windows-only dev-server issues, not exploitable on this macOS/Linux toolchain;echarts6.0.0→6.1.0 (runtime) clears #33 (XSS);js-yaml4.1.1→4.3.0 (runtime) clears #35 (quadratic DoS);golang.org/x/net0.53.0→0.55.0 (indirect) clears #34 (HTML-parser DoS). All non-major bumps. Verifiedgo build ./...,pnpm build, andvue-tsc --noEmitclean on both branches. -
2026-07-04 — Add two more
tech-stackcatalog 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/: 9architectureartifacts (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 9tech-stackartifacts (go-vue, ts-react-nest, python-fastapi, java-spring-angular, go-grpc-microservices, tauri, wails, electron, flutter). Addedarchitecture+tech-stacktoKnownTypes(internal/artifact/artifact.go) so they index cleanly. Each architecture declares compatible stacks viarelated_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 catalogREADME.mdindex; 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/integrationpackage, which had been red onmain: four agent-produced test files (from runscbcdfc8…/843e959…[killed-timeout] and7499574d…/9ce2fe6…[done]) were committed broken. (1)triage_watcher_regression_test.gocalled two hallucinated helpers — addedreadArtifactContent/writeArtifactContenttotriage_helpers_test.go. (2)devops_pipeline_run_history_integration_test.goimportedinternal/devopsunused — removed it. (3)agent_metrics_test.go'sTestReportsAgentUsage_AggregatedTTFSset up TTFT fixtures it never ran and asserted 300/500 against always-0 TTFTs — now persists TTFT viaSetAgentRunTTFT. (4)concurrent_run_under_contention_test.go'sTestConcurrentRunUnderHighLoadfired 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). Fullmake test-integrationgreen (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…rango test6×) 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-compiling7499574d…/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, planlifecycle/backend-plans/daemon-flag-usage-guide-3-be.md). M1: barekaos-controlinvocation now prints usage to stderr and exits 2 instead of starting the server. M2: added-d/--daemonflag (any position, stripped beforeflag.Parse); server start is now gated on-d,--daemon, orserve— bare-config <path>without-dis a usage error. M3: expandedusageconst with product description,-d/--daemonflag, all eight subcommands (devopsandreleaseswere missing), and the per-command help pointer. M4: updatedREADME.md(bare-start instructions × 3),Makefile(make runpasses-d), anddocs/end-to-end-smoke-tests.mdto reflect the new required-dflag. -
2026-06-27 — Fix
devops status <job-name>(defectdevops-cli-list-and-status-commandsM2): rewritecmd/kaos-control/devopscmd/status.goto require a positional job-name arg, callGET /api/p/{project}/devops/pipelines/{slug}/runs?limit=1for 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--jsonmodes. Updated the rootdevopsUsagestring to reflect the new signatures. -
2026-06-27 — Fix
devops list(defectdevops-cli-list-and-status-commandsM1): rewritecmd/kaos-control/devopscmd/list.goto callGET /api/p/{project}/devops/pipelinesand 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 run215bc5d8c2773b49: atest-developerrun burned ~58 min / $1.08 grinding through Claude's 10-attempt retry budget on transient401 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 detectapi_retry401 /authentication_failed(or the terminalis_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 dedicatedANTHROPIC_API_KEYvia theclaude-envdriver 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). MountsForceGraph3Dwithdag-mode+ a 2-cycle graph and asserts the component registersonDagErrorbeforedagMode, that the handler tolerates a reported cycle without throwing, and that neither is touched whendag-modeis absent (regular-map path). Verified the test fails ifgraph.onDagError(() => {})is removed. Frontend 1487/1487. -
2026-06-26 — Resolve 7 Dependabot alerts on
main. Bumped runtimemarkdown-it14.1.1 → 14.2.0 (#21, MEDIUM — quadratic-complexity DoS in the smartquotes rule; the only runtime/shipped dep of the set). Added a pnpmoverridesforundici^7.28.0inweb/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-tscclean,make build-websucceeds, frontend 1484/1484 green. Dependabot tracks the default branch, so these land onmainto 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 sharedForceGraph3D.vue, which registered noonDagErrorhandler — 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). Addedgraph.onDagError(() => {})beforedagMode()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-development→done). 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-2requirement 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 viabrainDump.createDoc, which setsstatus: 'raw'(the pre-draft quick-capture status from the raw-artefact-status feature) — but the test assertedstatus=draft. The app is correct; updated the assertion to expectraw. Flow 07 green 3/3. -
2026-06-14 — Stop gitleaks flagging its own documentation. The earlier
generic-api-keyfalse-positive fix (commit51d72d73) quoted the matched code inside both the.gitleaksignoreexplanatory 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 detectclean again — no leaks found. -
2026-06-14 — Fix a stale
RunDetailModal.test.tsassertion. The test expected a backdrop click to emitclose, but themodal-closes-on-outside-clickdefect (done, KC-Release3; commitfed90a6b) 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 devopstest-integration.yamlpipeline step) green — the only failures were the 5 docs-panel tests. 4 were test bugs:docs_list_test.go,docs_security_test.go, anddocs_write_test.go(×3) read the error code at top-leveldata["code"], butapiErrornests it asdata["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.Readmapped anysandbox.Resolveerror toErrPathTraversal, so a GET for a file under a non-existentdocs/dir returned400 path_traversalinstead of404(sandbox walks above the absent root and flags traversal); added anos.Stat(docsRoot)not-found guard mirroringdocs.List. All 20 docs tests pass; fullmake test-integrationgreen (0 failures). -
2026-06-13 — Get
make lintfully green (each fixed stage exposed the next). Bumped thego.modtoolchain directivego1.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-clienthttp.NewRequestcalls ininternal/agent/gemini.go— the request URL is built from the operator-configured base URL + model + API key, not user input, so addedG704to the Makefile gosec-excludelist with a justification comment (consistent with the existing G705 taint exclusion). That in turn exposed a gitleaks false positive:generic-api-keymatched the Go identifierdurApiMs = durApiMsN.Int64ininternal/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 ininternal/agent/gemini_cli_test.go(string context key → localctxKeytype); S1039 ininternal/http/releases.go(drop a no-argfmt.Sprintf); U1000 unusedreleasesDirininternal/release/disksync.goand unusedptr64ininternal/reports/agent_usage_test.go(removed); SA4006/SA4010 ininternal/triage/triage_test.go(thestartedslice 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 lintstill fails atgovulncheck— 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. Inweb/src/views/project/DocsEditorView.vue: (1) HTML —isHtmlnow matches thetext/htmlprefix (the backend'shttp.DetectContentTypereturns"text/html; charset=utf-8", so the old=== 'text/html'never matched), and the iframe renders a newhtmlSrccomputed thatatob()-decodesbody_base64(non-markdown responses carrybody_base64, notbody, 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 toin-developmentwith findings.vue-tscclean; 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 packageintegrationfrom different runs: renamedsetupFakeClaudeWithOutput→setupFakeClaudeWithLines(agent_ws),setupFakeClaudeWithScript→setupFakeClaudeWithRawScript(agent_metrics),seedAgentRun→seedAgentRunRow(agents_api), keeping the other variant canonical in each case. (2) Stub didn't satisfy the truncated-stream contract —setupFakeClaude(t,0)emitted nothing, so under the (now-enforced) truncated-stream detection a clean exit with notype:resultis correctly marked failed; the batch/lifecycle tests that expect a successful run were red.setupFakeClaudenow emits thesystem/init(bypassPermissions) +type:resultevents a real successful run produces (sharedfakeClaudeSuccessEvents); the two tests that deliberately want a zero-output run (TestSupervisor_NonClaudeRun_NoMetrics,TestAgentWSFinished_NoResultLine_ResultNull) use a newsetupFakeClaudeSilent; the defect-writing stubs emit the events too. (3) Backfill test schema — the hand-rolledagent_runstable inbackfill_metrics_test.gowas missing every metric +modelcolumn, so the backfill UPDATE hit "no such column"; added them to match the real schema. (4) Backfill idempotency — reverted thecmd/kaos-control/backfill.goquery tometrics_available=0only (the earlierOR model IS NULLwidening re-processed result-lines-without-modelUsage every run; the one-time re-stamp of already-backfilled rows is complete).go vet -tags=integrationclean; all previously-failing non-docs integration tests green. Remaining red: the 5TestDocs*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 thegeminiAPI driver and thegemini-cli/agydriver), andmobile-responsiveness.md— allstatus: done,release: KC-Release3, each linking to the relevant PROJECT_PLAN entries, source files, RELEASE_NOTES-0.1.3.md, and sibling lineages.mobile-responsivenesslinks to the existingmobile-responsiveness-followupsidea. Two of the requested features already haddoneideas:agent-usage-analytics-report(alreadyKC-Release3, untouched) andclaude-hooks-driver(the claude-mediated driver) — the latter was missing arelease:field, so addedKC-Release2to 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. Newinternal/reports/pricing.goadds 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 recordedcostUSDto 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.goaccumulatesTotalInputCostUSD/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 inpricing_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'stype:resultline (UpdateAgentRunMetricsdocuments it), butParseResultLinenever 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.gonow derives the primary model from the result line'smodelUsagemap (the entry with the most output tokens — Claude Code runs a cheaper background model, e.g. haiku, alongside the primary), exposed asRunResult.Model;agent.gowrites it into the run-finish metrics;cmd/kaos-control/backfill.gowrites it too and its query widened tometrics_available=0 OR model IS NULL/''so rows already backfilled (metrics-only) get re-stamped. NewTestParseResultLine_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/mainintokc-devand complete the Vite 5 → 6 / Vitest 1 → 4 upgrade intests/web. The only commit on main was a Dependabot bump of vitest to 4.1.0, which is incompatible with Vite 5 (vitest 4 importsvite/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 bringtests/webin line: added an explicitvite ^6.4.2(it had been pulling Vite 5 transitively), bumped@vitejs/plugin-vue^5.0.4 → ^5.2.4(peersvite ^5||^6), andvitest ^4.1.6. Then worked through Vitest 4's behavioural changes: (1) removedpoolMatchGlobs/poolOptionsfromvitest.config.ts— both removed in v4; v4 defaults the pool toforks(one per file) so perf-file isolation is preserved automatically. (2) Fixednew ResizeObserver()mocks in the three 3D-graph test files — v4 forbidsnewon avi.fn()with an arrow implementation, so switched to a regularfunction. (3)queueStoreFS3 tests: v4's microtask flush now lets the post-event_silentRefresh()resolve withinawait 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)ArtifactRunHistoryreactive-update test: same class — theonMountedfetchRunsByTargetPath()now resolved mid-test and overwrote the manual$patch; hang the fetch. (5)RoadmapView.periodModeM8.3: v4'svi.restoreAllMocks()no longer clearsvi.fn()call counts (only spies), sogetConfig's count accumulated across the file — addedvi.clearAllMocks()to the describe'sbeforeEach. (6) Leaked-fetch unhandled rejections are now fatal in v4 — mocked@/stores/projectinQueueView.test.ts(itsonMountedcallsfetchProjects()) and@/api/agentsinartifact-blocked-questions.test.ts(the editor callsfetchAgents()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
navItemslist had outgrown a single column, so web/src/components/layout/AppSidebar.vue now builds anavSections: { 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-itemwith a.nav-link, so badges (Parse Errors count, Testing approved count) and active-route styling are unchanged. Tests: updatedexpectedLabels/allExpectedLabelsto the new section order and the per-view nav-link count; added afunctional section groupingdescribe asserting the four headers render in order and that headers carry no link and aren't.nav-items. Frontend 1484/1484 green;vue-tscclean;make build-websucceeds. -
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 rawt.TempDir()path viaExpectedEvents.Expect, butReleaseHandler.Handleresolves symlinks (EvalSymlinks) beforeConsume, and in productionDiskSync.Writerecords thesandbox.Resolve-resolved path — so on macOS the temp dir's/var → /private/varsymlink made the keys diverge and the API-originated write was not suppressed (spuriousrelease.changedWS event). Linux CI passed because/tmpisn't symlinked. Fixed by resolving the path beforeExpect, mirroring production. (2) FrontendAppSidebar.test.ts— the sidebar gained two nav items from concurrent work,Reports(8461c390) andDocumentation(5224cae0); the test'sexpectedLabels/allExpectedLabelsarrays (13) and the per-viewnav-linkcount assertion (hard-coded 13) were stale. Updated to the 15-item set in component order. (3) Frontendreleases-api-unwrap.test.ts— the releases API client now mapsfile_pathandslugonto every unwrappedRelease(web/src/api/releases.ts); the test'ssamplefixture omitted them, sotoEqualfailed. Added both fields. Full backend-shortsuite 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 onagent_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 idempotentALTER TABLE; two new covering indexes (idx_agent_runs_started_at,idx_agent_runs_agent_name);AgentRunRowextended with pointer fields and all scan/select helpers updated. M2: on run finish, parse the log viaagent.ParseResultLineand write cost/token metrics plusmetrics_available=1via newindex.UpdateAgentRunMetrics; stamp the requested model viaindex.SetAgentRunModelimmediately after insert; newindex.SetAgentRunTTFTfor time-to-first-token persistence. M3: TTFT capture —isFirstContentTokenhelper detects the first{"type":"assistant"}event with a non-empty text block;OnTTFT func(ms int64)callback onRunstruct wired by the Manager for streaming drivers. M4:kaos-control backfill agent-run-metrics --project <id>one-off command incmd/kaos-control/backfill.go— queriesmetrics_available=0terminal rows, parses each log, writes metrics;--dry-runflag; safe to re-run. M5:internal/reports/agent_usage.go—BuildAgentUsageReportruns one SQL SELECT, streams rows into per-dimension accumulators, computes median/p95 via sort, fills zero-run buckets, returnsAgentUsageReportwithsummary(overall/per_model/per_agent) andseries/series_by_model/series_by_agent. M6:GET /api/p/:project/reports/agent-usageroute registered under the existing project-scoped chi group;internal/http/reports.gohandler 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 theasuffix fromVERSIONso it reads0.1.3(bare semver matching^[0-9]+\.[0-9]+\.[0-9]+$).TestVersionFile_ExistsAndIsValidSemverrequires 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.go—triageCfgYAMLconstant,installLLMFake/installLLMFakeErrorswappingideachat.CallLLM,defaultProposeJSON,writeRawIdea,readArtifactFM,pollForArtifactStatus,pollForRunStatus,newTriageTestEnv/newTriageTestEnvWithSeeds. Production change:internal/ideachat/llm.goCallLLMconverted from named function to package-levelvarpointing tocallLLMImpl, enabling test injection without build tags. M2:internal/triage/eligibility_test.go— addedTestEligible_WrongStatus_ClarifyingandTestEligible_CaseSensitivity(capital-RRaw→wrong_status). M3:internal/triage/run_test.go— 10 tests coveringrewriteBody(fresh triage, re-run idempotency, no-H1, agent-H1 strip, title preservation),mergeAndFilterLabels(merge+dedup+vocab-filter), priority defaulting, andmarshalArtifactround-trip. M4:internal/triage/triage_test.go— addedTestTrigger_LockReleasedOnFailureverifying 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 Ideablock). 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, slogcaptureHandlerverifyingtriage failedrecord has path/lineage/reason fields). M9:lifecycle/tests/auto-triage-new-ideas-6-test.mdcompanion 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: addedweb/src/api/ideas.tswithtriageIdea(project, slug)typed wrapper forPOST /ideas/{slug}/triage; Vitest unit tests (success/409/401); vitest jsdom environment wired intovite.config.ts. M2: newTriageNowButton.vuecomponent — visible only fortype=idea/status=rawartifacts viewed by users withproduct-owner,analyst, orreviewerroles; usesartifact.lineageas slug; shows inline ApiError reason on failure; emitstriage-startedwith the run_id.ArtifactEditorView.vuerenders the button adjacent toQueueWorkButton;onTriageStartedhandler immediately refreshes the artifact run history. M3: verification pass —WorkspaceViewalready routesAGENT_EVENTS(includingagent.started) toagentsStore.onWsEvent;agentsStoreauto-refreshesartifactRunswhen eventtarget_pathmatches; no new code needed. M4: updatedrawstatus pill inStatusDropdown.vueto orange (#ffedd5/#c2410clight,#431407/#fb923cdark) to distinguish it from the near-identical grey used bydraft;rawis already present inArtifactListViewstatus filter dropdown. All milestones:vue-tsc --noEmit+pnpm build+pnpm test(31/31) pass. -
2026-06-05 — Fix false
truncated_streamfailures introduced by the 2026-06-02 detection. A user-supplied run log from sol.packsin.com showed aqa(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 atinternal/agent/agent.gogates onresultEventSeen, which is only set inside thebroadcastclosure for events actually read off the progress channel. ButrunPrecheck/runMediatedPrecheck(internal/agent/precheck.go) return the instant they see thesystem/initevent, andsupervisehad no drain loop on the precheck-pass path — so every post-init event, including the terminalresult, sat unread in the buffered (cap 64) progress channel.resultEventSeenstayed false and every clean Claude run was downgradeddone → 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 afterinit). Fix: added a drain loop after the precheckswitchthat forwards remaining events through the existingbroadcastclosure (setsresultEventSeen, feeds the WS view, removes the stall); extracted a sharedprogressPayloadhelper now used by both precheck loops and the supervisor drain. Newsupervise-level regression tests (TestSupervise_ClaudeRunWithResultEventMarkedDone/…WithoutResultEventMarkedFailed) drive a fake process emittinginit+resultand assertdone, whileinit-only still yieldsfailed; verified the positive test fails on the unfixed code and passes after. Defect raised atlifecycle/defects/agent-run-false-truncated-stream-failure.md. The original detection's unit tests only exercised the predicate functions in isolation, so nothing drovesuperviseend-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 thesedonedespite the agent's task being incomplete — exactly the user's "didn't finish well with no errors" report. Fix ininternal/agent/agent.go: trackresultEventSeenvia the broadcast closure (isResultEventpredicate); for drivers that emit the stream-json terminal contract (claude-code-cli,claude-mediated, gated bydriverEmitsResultEvent), a clean exit without a result event now downgradesdone→failedwithfailure_reason="truncated_stream". Reason flows through the existingagent.failedWS 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. NewTestDriverEmitsResultEvent(9 cases) andTestIsResultEvent(6 cases) lock the predicates. Full Go unit suite green. -
2026-06-02 — Two-part follow-up to the 529 fix. (A) Surface
api_retryevents 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.formatEventinweb/src/stores/agents.tsnow recognises{type:"system", subtype:"api_retry"}and renders↻ retrying after 529 (attempt 3/10, 2.3s backoff)lines into the progress log. Newtests/web/agentsStore.apiRetry.test.tscovers 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'sFallbackPause(30 min default) was tuned for quota/hourly rate-limits — too long for transient overload. AddedOverloadPause(5 min default) and threaded akindclassifier through:extractRateLimitTextnow returns aRateLimitKind(rate_limit|overloaded), the supervisor'squeue.rate_limitbroadcast includeskind, the dispatcher'srunResultcarriesrlKind, andhandleRateLimitpicksOverloadPausewhenkind=="overloaded"and the rawText has no parseable reset. NewTestDispatcher_OverloadPauseUsedForOverloadedKindlocks 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
extractRateLimitTextso 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. Addedoverloadedand\b(429|529)\bpatterns to thequotaExhaustedREregex; three new test cases lock the 529, 429-positive, and 500-negative cases (HTTP 500 stays a hard failure, not a retry). All twelveTestExtractRateLimitTextsubtests green. -
2026-06-01 — Mobile responsiveness pass (M1–M6). Investigated baseline state — only ~12% of Vue files had any
@mediarules, 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: 1024pxtokens,useViewport()composable, global mobile helpers (touch-target floor, iOS-Safari font-size bump,.table-scrollutility); (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 atlifecycle/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/mainintokc-devto roll up Tim'scodex-clidriver 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 +.gitignorerepair). Resolved conflicts in.gitignore(keep.unison*from kc-dev plus.pnpm-store//**/__debug_bin*from main),internal/agent/agent.go(rebase thewaitErr chan errorfield added on kc-dev onto theclaudeProcess→cliProcessrename from main; carry the field onto the renamed type),internal/agent/gemini_cli.go(rename in-file references tocliProcess), fourweb/src/components/agent/*files (addclaude-mediated,gemini,gemini-clito the radio set alongside the newcodex-cli), andplans/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'sLICENSE/README.md/*.d.ts/*.js/package.json— kept showing up ingit statuswhenever 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. Verifiedgit check-ignore -vnow resolves all four rules cleanly. -
2026-05-30 — Fix
GanttChart's autoscale-modeclippedRightoff-by-one that lit upM5.4: autoscale mode produces no clipped bars (bars always fit axis)when TODAY landed on May 30. Withgranularity=month,endOfGranularity(maxEnd, 'month')returns local-midnight of the last day (e.g. June 30 00:00). The bar check wasaddDays(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 bothaddMonths(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: introduceendOfDay(d)returning 23:59:59.999 of the local day, and change the check toclippedRight: e > endOfDay(rangeEnd). This is also timezone-robust — release dates parsed vianew Date('YYYY-MM-DD')are UTC-midnight (= 10:00 in AEST), so the naivee > rangeEndwould 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 probecodexExecSupportsTimeoutruns<binary> exec --helpwith a 2 s context deadline to sniff for the--timeoutflag. 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 realcodexbinary 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 templateinternal/initcmd/templates/config.yaml.tmplhad twokanban: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, soconfig.LoadProjecton a freshly-initialised project failed withmapping key "kanban" already defined at line 66andTestConfigTemplateLoadsCleanlywas red. Deleted the second block;go test ./internal/initcmd/...green. (One intermittent flake remains inTestCodexExecSupportsTimeout— 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-driversagainst KC-Release3.web/src/components/agent/AgentConfigForm.vueonly offersclaude-code-cli,codex-cli, andollamaradio options, but the backend (internal/agent/agent.go:405-412) also registersclaude-mediated,gemini, andgemini-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-cliin the agent UI.AgentConfigForm.vuenow 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.tsdocuments the new driver value.pnpm -C web run type-checkpasses. -
2026-05-27 — Add backend
codex-cliagent-driver support. Newinternal/agent/codex_cli.gorunscodex exec --json --dangerously-bypass-approvals-and-sandbox, passes--cd <project-root>for workspace correctness, optionally mapstimeout_minutesto--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 fromclaudeProcesstocliProcessand now runscmd.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/configand the live smoke pass. -
2026-05-22 — Override agy's
--print-timeoutso the agent isn't cut off mid-reply at the default 5-min wait. With--add-dirfinally pointing agy at the project, run87a65b0a1acf0647actually did the work — ran the test suite and started writing the report — but agy aborted at exactly 5m6s withprintmode.go:263 Print mode: timed out after 1495 polls (printed=41), which surfaces in our log asError: timed out waiting for response.buildArgsnow passes--print-timeout <TimeoutMinutes>mwhen the agent config sets a positive timeout, and--print-timeout 24hwhen 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 recordsworkspaceDirs=[]and defaults to~/.gemini/antigravity-cli/scratchregardless of the parent's CWD — the agent spends the entire 5-min--print-timeoutbudget hunting for the workspace ("I will list the parent directory…") and then exits withError: 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 lineCreating CLI server backend: product=antigravity workspaceDirs=[].buildArgsnow inserts--add-dir <ProjectRoot>between--dangerously-skip-permissionsand--prompt; the existing test split intowithProjectRoot/withoutProjectRootsubtests, both green. -
2026-05-22 — Fix
gemini-clidriver hang whereagyexited but the agent stayed markedrunninguntil the user manually killed it. Symptom: DB showed run512ea0e72bddf114at 6m48s duration even though the log captured only an instantError: timed out waiting for responsefrom agy on stderr. Root cause:agydetaches 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 calledproc.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 ininternal/agent/gemini_cli.go: runcmd.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 bufferedwaitErr chan erroronclaudeProcess(renamed tocliProcesspost-merge with main), and havecliProcess.Wait()read from that channel when set (existing Claude-driver behaviour is unchanged because that field stays nil for them). NewTestGeminiCliDriver_DetachedChildHoldsPipesreproduces 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 frominternal/initcmd/templates/config.yaml.tmpl. The template had twokanban: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, soconfig.LoadProjecton a freshly-initialised project returnedmapping key "kanban" already defined at line 66andTestConfigTemplateLoadsCleanlyfailed. Deleted the second block; full suite green (make test-unit,make test-integration182s,tests/web1452/1452,make test-e2e23/23). -
2026-05-22 — Fix
claude-mediatedregression where every tool call returned"malformed server response"(runfb1503454e5e6658). Root cause: commit910582a9correctly updatedinternal/http/permission_hook.goto emit Claude's canonical{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"…","permissionDecisionReason":"…"}}shape, butcmd/kaos-control/hookcmd/hook.go's response parser was still looking for the old top-level{"decision":"…","reason":"…"}envelope —json.Unmarshalsucceeded against the new shape butserverResp.Decision == "", so every response fell intowriteDeny("malformed server response"). The integration tests intests/integration/hook_helper_test.gomasked this because every stub was still emitting the old{"decision":"…"}shape. Fix: hook-helper now validates the canonical shape parses with a non-emptyhookSpecificOutput.permissionDecisionand 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 8TestHookHelper_*+ 6TestPermission_*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}/initandkaos-control inithad diverged: GUI wrote an agentlessconfig.yamlfromyaml.Marshal(defaultProject())with an outdated stage list (haddev-plans/sprints, missingdocs), no seed files (CLAUDE.md,.claude/settings.json,.gitignore,devops/sample.yaml), and nousers:entry — leaving the logged-in user with zero project roles, blocking every workflow transition viaRolesFor(user.Email). Refactoredinternal/initcmdto expose a reusableScaffoldProject(ScaffoldOptions)function that does the file/dir creation; CLIRunnow delegates to it (keeping its flag parsing + auth-DB user creation as a wrapper). RewrotehandleInitProjectto pulluser.EmailfromuserFromCtxand call the sameScaffoldProject— GUI now writes identical layout including the full template (10 agents/roles/stages) with the session user auto-populated inusers:. Retired the now-unused publicconfig.DefaultStages()/DefaultProjectConfigYAML()(privatedefaultStages/defaultRoles/defaultProjectkept as in-memory fallbacks forLoadProject). UpdatedInitProjectModal.vuepreview to list the actual scaffold (was missing prototypes/tests/docs/devops/seed-files, and still listedsprints). Updatedtests/integration/projects_crud_test.goto assert landmark files exist + the session user's email appears in the renderedconfig.yaml; updatedtests/cli_init_test.goto match the new stage list (dropsprints, adddocs/lifecycle/devops/top-leveldevops, adddevops/sample.yamlto the seed-file list). All four suites green; CLI + GUI scaffolds verified identical by smoke test. -
2026-05-18 — CLI polish:
--versionflag plus better unknown-flag handling.cmd/kaos-control/main.gogains a--version/-version/-Vcase 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. Thedefault:clause previously let any leading-dash argument fall through toflag.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-configflag still falls through torun()so the implicitkaos-control -config /pathserve 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,
IndexFilewas callinggit.Repo.FirstCommitDatefor every artefact lacking acreated:frontmatter — go-git'scommitPathIteris 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 misleadingfatal: context deadline exceededfrom the 10shttpSrv.Shutdownbudget on a never-started server. Three changes: (1)internal/index/index.godrops theFirstCommitDatefallback inIndexFile— uses mtime as the single fallback when frontmatter has nocreated:. The git path was used to drive exactly one UI element (the artifact list's Created column); mtime is good enough and instant. (2) Newkaos-control backfill-createdsubcommand (internal/backfillcmd/) walkslifecycle/**/*.mdand insertscreated:into the frontmatter using filesystem birth time (DarwinStat_t.Birthtimespec; mtime fallback elsewhere). Atomic temp+rename writes;--dry-runand-vflags; skips files that already havecreated:and files without a frontmatter block. (3)cmd/kaos-control/main.gonow 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 inittemplate to canonical project config so newly-initialised projects ship the same lifecycle as kaos-control itself.internal/initcmd/templates/config.yaml.tmpl: addedapprover,devops,tech-writerroles (10 total); droppedsprintsstage, addeddocs(10 total); addedtech-writer,test-runner,docs-captureagent blocks ported from the canonical config (10 total); annotatedallowed_write_pathson 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 emitsTODO@example.com+ a 3-line comment explaining every project needs at least one user.scaffold.godropslifecycle/sprintsand createslifecycle/docs.CLAUDE.md.tmpldirectory tree updated to match.initcmd_test.goexpectations 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-gapidea. README updates: status line bumped to "pre-1.0 (v0.1.x), working releases"; lifecycle-stage list now includesprototypes; 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 withclaude-code-cli-scoped setup steps and a "Detection at run-start" subsection replacing the stale "Coming in KC-Release1" callout; version pinned to0.1.2in install examples (was a mix of0.1.3/0.1.1); PATH-instructions formatting fixed; first-run config example now shows theagent:block;Where things livetable gains a per-agent-run-log entry. New idealifecycle/ideas/frontend-lint-gap.mddocuments the JS/TS lint gap (no ESLint/Prettier;vue-tscnot inmake lint) and proposes a two-stage fix (wirepnpm run type-checkintomake 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 (commit30a6e6a9) — 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) AddeduseWebSocket(project, 'agent.started', …)toArtifactListView.vueso the "Agent Running" pill actually appears mid-run; previously the list only refetched onartifact.indexedandagent.finished, so fast runs finished before the pill could appear. (C) Test-side patches:tests/e2e/harness/ws.tsaccepts acookieHeaderand 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?.statusnotdata.status), usegetByRole('columnheader', { name: 'Runs' })instead of ahasTextregex that the SortHeader's icon-suffixed<th>text broke, and click the new modal-Edit button now that map node taps openArtifactModalinstead 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#94a3b8text on#f1f5f9(≈3:1 contrast, fails WCAG AA), and the type pills (IDEA / DEFECT) had no dark-mode story at all. Switched to the samedata-status-driven palette already used byStatusDropdown.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-levelActive/Planned/Shippedbadge got the same treatment. Removed the now-unusedartifactStatusBadgeClasshelper. Note: palette is duplicated between this file andStatusDropdown.vue— worth a follow-up to extract toweb/src/styles/status-badges.cssif 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.
extractRateLimitTextpreviously only detected two stream-json shapes (error:"rate_limit"anderror.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 asagent.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 istype:"result"withis_error:trueandresulttext matches a small set of quota phrases (out of usage,usage … resets,rate.?limit,message limit,exceeded … quota/limit) we return the result text — whichParseResetTime's pattern 3 already parses forresets HH:MMpm (Area/City). Three new test cases inTestExtractRateLimitTextlock 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.
watchRunEventswas matching on event type only (agent.finished/agent.failed/queue.rate_limit) without checking the payloadrun_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'sStartRunwould fail withlineage X is locked by …. Added arunIDChchannel:processNexthands the runID to the watcher afterStartRunreturns, the watcher drops terminal events with mismatchedrun_id, and drops them entirely until the runID arrives (since by definition they cannot be ours). NewTestDispatcher_IgnoresForeignFinishedEventinjects a foreignagent.finishedmid-run and asserts the dispatcher still completes both queued jobs in sequence. -
2026-05-15 — Backend plan
artefact-relationship-labels-and-links-3-be.mdMilestone 1: extractedEdgeKind*string constants (EdgeKindParent,EdgeKindDependsOn,EdgeKindBlocks,EdgeKindRelatedTo,EdgeKindMembers,EdgeKindWiki,EdgeKindAssigned,EdgeKindTimeline) intointernal/artifact/artifact.go. Replaced all string literals inextractLinks()andinternal/http/releases.gowith the new constants. Milestone 2 verified:GET /api/p/:project/graphalready returnssource,target,kindon everyGraphEdgeandid(file path) on everyGraphNode— no API changes required.go build ./...+go vet ./...pass. -
2026-05-15 — Fix
claude-mediatedhook response schema. The permission endpoint and thehook-helperfallback paths were emitting{"decision":"allow"}, but Claude Code's PreToolUse hook contract is{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"|"deny"|"ask","permissionDecisionReason":"…"}}. Without thehookSpecificOutputenvelope Claude ignored the response and fell back to interactive permission prompts — visible in the user-facing run as our policy saying "allow" inpermission_decisionlog entries while Claude's stream-jsontool_resultfor the sametool_use_idreturned "you haven't granted it yet". Updatedinternal/http/permission_hook.goto wrap responses via a newnewHookResponsehelper, andcmd/kaos-control/hookcmd/hook.go'swriteResponseto emit the same envelope on every error path. Updatedtests/integration/permission_endpoint_test.goto read fromhookSpecificOutput.permissionDecision(plus asserthookEventNameandpermissionDecisionReasonshape). -
2026-05-15 — Resolve absolute file paths against project root in
policy.Evaluate. Claude Code sends absolute paths inPreToolUsetool_input (e.g./Users/keith/Code/kaos-control/lifecycle/requirements/foo.md); the policy was justTrimLeft("/")ing them, leavingUsers/keith/…which obviously never prefix-matches the project-relativeAllowedPathslikelifecycle/requirements. AddedProjectRoottoPolicyConfig(populated fromm.root);Evaluatenowfilepath.Rels absolute paths to project-relative before matching, and denies withrule=outside_projectfor absolute paths that escape the project root or whenProjectRootis 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-mediateddriver's hook config so Claude actually invokes the helper. The generatedsettings.jsonwas using a flatPreToolUse: [{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-pruns 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 (nopermission_decisionlines in run logs). Addedinternal/agent/settings_test.goto lock the matcher-wrapped schema. Diagnosed from run207bdbaab07e22e0where three identical Write retries all returned Claude's own approval-needed text rather than any policyRulefield. -
2026-05-13 — Make
OllamaDriverrun logs matchClaudeCodeDriver's information density: (A) Header now includes# system_prompt:/# user_prompt:blocks (Claude logs the prompt viaargs=-p "<prompt>"; Ollama was missing it entirely). (B) NewformatOllamaSummaryhelper captures the finaldone:trueNDJSON 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 viatime.Duration.Round(ms), tok/s derived fromeval_count / eval_duration. Gracefully omits segments when stats are absent (older Ollama versions, generate vs chat differences). Two unit tests ininternal/agent/ollama_test.golock the format. -
2026-05-13 — Teach
OllamaDriverto write its per-run log file. The driver was ignoringrun.LogPathentirely, 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 theClaudeCodeDriverconvention: openrun.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: completedmarkers plus the accumulated full-response text on success; always write a# finished=<rfc3339>footer viadefer(so cancellation/error paths still close cleanly). -
2026-05-13 — Fix
precheck_timeoutfailures for Ollama (and any non-Claude) agent runs.supervise()ininternal/agent/agent.gowas unconditionally callingrunPrecheck, which waits for asystem/initevent carrying apermissionModefield — that event is part of Claude Code's stream-json protocol, so every Ollama run hitinitEventTimeoutand failed withobserved_permission_mode="". ThreadedDriveronto theRunstruct (populated fromag.Driver);supervise()now branches:claude-code-clikeeps 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.goLoadApp: when a config YAML exists but omitsprojects_dir,cfg.ProjectsDirstayed empty and the unconditionalos.MkdirAll(cfg.ProjectsDir, 0o700)introduced in10b80f24failed withmkdir : no such file or directory. Added a path-relative fallback (<config-dir>/projects) matching the one already in place forDataDir. FourTestOllamaConfig_*/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 onmockResolvedValueOnceto satisfyfetchParseErrors, but two later additions on 2026-05-06 (GitStatusBarchild mount →api.get('/git/status'), andtestingStore.fetchApprovedCount) made parse-errors no longer the firstapi.getcall (childonMountedfires before parent). Added avi.mock('@/components/layout/GitStatusBar.vue', …)no-op stub alongside the existing module mocks. (B)tests/web/QueueView.projectNav.test.tsM5-3 —QueueView.onMountednow strips?project=<unknown>viarouter.replacewhen the name doesn't match a known project, matching what the test explicitly documented as expected. (TheQueueView.vuechange 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
a61b882frenamedQueueJob.agent→QueueJob.agent_name:tests/web/QueueView.test.ts(helper + one override site) andtests/web/QueueView.projectNav.test.ts(helper) now useagent_name. The templates readjob.agent_nameso the rendered Agent value was undefined; tests assertingtoContain('requirements-analyst')failed. Full suite: 1358/1358 pass. -
2026-05-13 — Remove dead
nonInitEventhelper frominternal/agent/precheck_test.go.staticcheck U1000flagged it viamake lint; the test that originally needed it ended up constructing events inline, so the helper was unreferenced. Five-line deletion;make lintclean. -
2026-05-13 — Queue UI discoverability. (A)
AppHeader.vuequeue 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--idlemodifier 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/queueroute, between Agents and Scheduler with theListChecksicon. Test fixtures updated:AppHeaderQueueBadge.test.ts(two FH1 cases now assert the idle badge +--idleclass),AppSidebar.test.ts(three label-array expectations bumped 12→13 to include Queue). Also fixed two staleQueueWorkButton.test.tsassertions 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.shbundles each cross-compiled binary indist/into a versioned zip (kaos-control-<VERSION>-<os>-<arch>.zip) containingkaos-control/{kaos-control[.exe], README.md, LICENSE, CONTRIBUTING.md}with the binary's 0755 mode preserved. Also writesdist/SHA256SUMS(auto-detectssha256sumvsshasum -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. Newmake packagetarget depends onreleasesomake packageis the one-line release-pipeline call. README gains a "Note on Claude Permissions" section explaining the one-timeclaudebypass-acceptance step that's required on every machine before agent runs work (the issue Ben hit on 0.1.0)..gitignorenow excludes/support/so user-log drops there stay out of the repo. -
2026-05-12 — Fix defect
default-config-data-dir-incorrect:LoadAppininternal/config/config.gonow setsDataDirto<config-dir>/databefore callingSaveAppon first run, so the generatedconfig.yamlcontains a correctdata_dirvalue. AddedTestLoadAppDefaultDataDirto verify the persisted value survives a reload.go build ./...+go vet ./...pass. -
2026-05-12 — Fix defect
default-config-port-should-be-8042:defaultApp()ininternal/config/config.gonow uses:8042as 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:LoadAppininternal/config/config.gonow writes the default config to disk (viaSaveApp) when the file doesn't exist on first run; also sets a sensibleprojects_dirdefault (<config-dir>/projects) before persisting. New installs have a concrete~/.kaos-control/config.yamlto inspect and edit without manual setup. -
2026-05-11 — Commit
internal/index/index_test.go(8-case unit-test forindex.Countcovering status+type, status-only, type-only, CSV-type-OR, and the no-filter total). Was left uncommitted by an earlier agent run during thesource_typeswork; 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-plansfor test-developer instead of&type=plan-test). Added asourceTypeToStagemap inAgentPanelRow.vuecovering 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 offetchReadyCountswas the WebSocketartifact.indexedhandler in WorkspaceView — on a cold load no event has fired, so the store stays empty. Added an initialvoid store.fetchReadyCounts(project)toAgentsRunsView.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. FixedAgentPanelRow.handleBadgeClickto usestatus=approvedso 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 (approvedstatus) still left the badge under-counting for developer agents. TheAgentLaunchModalfor any plan-* developer agent also lists approved defect artifacts whosefrontmatter.assignees[].roleincludes the agent's role.handleGetReadyCountsininternal/http/agents.gonow mirrors that behaviour: for agents whosesource_typescontains anyplan-*entry, it lists approved defects and adds those whose assignees match the agent's roles. New integration testTestReadyCounts_DeveloperIncludesAssignedDefectscovers 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-specificdefect. The ready-count badge on the Agents screen was counting artifacts in each agent'sactive_status(e.g.in-developmentfor test-developer) — butactive_statusis the during-run status the agent transitions the artifact INTO, not the picking-from status. The agent driver picks up artifacts in statusapproved, which is also whatAgentLaunchModalshows when the user clicks an agent. Switchedinternal/http/agents.go:handleGetReadyCountsto count byStatus: "approved"(namedreadyInputStatus), keeping thesource_typesfilter intact. Three integration test files (ready_counts_test.go,agent_panel_status_test.go,agents_ready_counts_smoke_test.go) updated to seedapprovedartifacts and reflect the corrected semantic;agentPanelCfgYAMLgainedsource_typesonagent-with-model/agent-no-modelso 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-mutationsMilestone 8 (post-bootstrap user-create requires product-owner) and the earlier WS-auth exemption fromrequireAuth: (1)tests/auth_middleware_test.goTestWebSocketAuth_Rejectednow accepts 401 or 404 — with/wsexempt fromrequireAuth, an unknown project falls through toprojectMiddlewareand surfaces as 404. (2)TestBearerAuth_SkipsCsrfnow inspects the response body's error code; a 403 withcsrf_missing/csrf_invalidis the only failure mode, since Milestone 8 introduced a separate handler-level 403 (forbidden) for non-product-owner callers. (3)internal/http/agents.go:76error code aligned tonot_found(matches the convention everywhere else in the file and inartifacts.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.gonow auto-logs in asadmin@test.localinnewTestEnvFulland exposes anenv.logout()helper for tests that verify the 401 path. Bulk-converted ~30http.Get(env.baseURL + …)call sites toenv.doRequest("GET", …, nil)across 15 test files; stripped now-unusednet/httpimports. Updatedtests/integration/priority_roundtrip_test.goso concurrent goroutine readers attach session cookies viahttp.NewRequest+AddCookie. Two real production bugs surfaced in the process:internal/http/auth.gocsrfMiddlewarenow skips CSRF for unauthenticated requests so they fall through torequireAuthand get a clean 401 (was returning misleading 403csrf_missing);internal/http/devops.gohandleCreatePipelinewas writing to<root>/devops/<slug>.yamlwhile the list handler read<root>/lifecycle/devops/— switched POST todevopsDir(). 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-chartandactivity-feedare now special-cased into the dedicatedsection[aria-label="Velocity and activity"]row bySIDE_BY_SIDE_IDSinDashboardGrid.vue, so the panels and bottom-charts containers no longer hold them. -
2026-05-10 — Debounce kanban board's
artifact.indexed→refresh()handler inweb/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/.../artifactstraffic. -
2026-05-10 — Fix board-loading regression introduced by global auth middleware.
requireAuthininternal/http/auth.gonow 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.gonow 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 itslimit=5000to 500: rewrote to match the backend cap (PAGE=500), advance offset by actual rows received, and break on a 0-row page..gitleaksignoreadded to suppress one false-positive (RFC 6455 example WebSocket nonce in tests). -
2026-05-10 — Documented
blockedas a valid status in CLAUDE.md (was already inKnownStatusesatinternal/artifact/artifact.go:34but 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: addedgovulncheck,gosec, andgitleaksto the lint pipeline. Bumped Go viatoolchain go1.25.10directive ingo.modto clear 19 reachable stdlib CVEs (now 0). Hardened logout cookie hygiene ininternal/http/auth.go(HttpOnly/Secure/SameSite on cleanupSet-Cookie).gosecexclusions 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.mdMilestone 1 verified: no Go source changes required.go build ./...andgo vet ./...pass. All "Graph" references ininternal/are internal type names (GraphNode,GraphEdge,GraphData), method names (handleGraph,buildRoadmapGraph), and theIndex.Graph()query method — none are user-facing strings. TheGET /api/p/:project/graphendpoint 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.mdMilestone 2 verified:KnownStatusesininternal/artifact/artifact.godefines the canonical vocabulary (draft, clarifying, planning, in-development, in-qa, approved, rejected, abandoned, done, blocked).StatusDistributionexcludesdone/abandonedand only returns statuses stored in the DB — every returned value is a member ofKnownStatusesfor well-formed projects. No discrepancy found; no defect raised. No code changes needed. -
2026-05-09 — Backend plan
dashboard-clickable-filters-3-be.mdMilestone 1 verified:GET /api/p/:project/artifacts?status=<s>correctly filters by any status value.buildWhereininternal/index/index.goappliesstatus = ?exact-match with an index-backed column (idx_artifacts_status).StatusDistributionreturns raw DB status values, all of which are valid filter parameters.GET /artifactswith 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 initfor 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 returnstotal_tickets); StatusDistributionWidget readdata.items(backend returnsdata.distribution); VelocityChartWidget readdata.items(backend returnsdata.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_typesfield inlifecycle/config.yaml(default["ticket"]).internal/index/index.goDashboardStats,StatusDistribution, andCompletionVelocitynow take a types list via a newtrackedTypesClausehelper.internal/http/dashboard.gothreadsp.Cfg.Dashboard.TrackedTypesthrough. kaos-control's own bootstrap setstracked_types: [requirement, idea, defect]. Fixes the dashboard returning all-zeros on projects whose work-item type isn'tticket. -
2026-05-06 — Backend plan
test-artifact-management-3-be.mdMilestone 5 confirmed: Kanban "Show Tests" default-off toggle is a frontend-only concern;GET /api/p/:project/artifactsreturns 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.mdMilestone 4 verified:agent.finishedandagent.failedbroadcast insupervise()(internal/agent/agent.golines 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.mdMilestone 3 verified:supervise()ininternal/agent/agent.gocorrectly orders: semaphore released → git commit → lock released (line 575) →UpdateAgentRunto 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.mdMilestone 2: addedCount(filter Filter) (int, error)tointernal/index/index.go(runsSELECT COUNT(*)with the samebuildWherelogic, no pagination); addedcount_only=truequery-param branch tohandleListArtifactsininternal/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.mdMilestone 1 verified:CREATE INDEX idx_artifacts_type ON artifacts(type)exists increateSchema()atinternal/index/index.go:1385;buildWherealready handlestype = ?filter correctly.GET /api/p/:project/artifacts?type=testis index-backed. No code changes needed. -
2026-05-06 — Rename analyst agents to phase-first convention:
analyst-requirements→requirements-analyst,analyst-planner→planning-analyst. Updatedlifecycle/config.yaml(names + git identities),internal/workflow/workflow_test.gocomments,CLAUDE.mdagent listing, all three plan docs, and 14 lifecycle artifact body texts.make build,make lint,make test-unitall pass. Plan:lifecycle/backend-plans/rename-analyst-agents-3-be.md. -
2026-04-29 — Backend plan
editor-live-refresh-on-disk-change-3-be.mdMilestone 3 verified:GET /api/p/:project/artifacts/*pathininternal/http/artifacts.gocomputessha256.Sum256(raw)and returnsfile_shain 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.mdMilestone 2 verified: 150 ms debounce inwatcher.goresets timer on each fsnotify event and fires exactly onefile.changedper 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.mdMilestone 1 verified:file.changedWS event already includes{"path": "<relPath>"}ininternal/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.mdverified: all three milestones confirmed no-code (existing API fully supportsstatus=approvedandtype=combined filtering;frontmatter.assigneespresent 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 /artifactsaccepts assignee with emptywhofield (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.mdcreated 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/artifactparser,internal/indexSQLite layer,internal/projectcontainer, 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/meon 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 build→web/dist/→ embedded by Go binary..gitignoreupdated:/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 spawningclaude --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/…), gitModifiedFilesfor scope-enforced post-run commit, index CRUD foragent_runsandlineage_lockstables, lock reaper wired into startup.lifecycle/config.yamlextended 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.vuerewritten: 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.ModelthroughRun.Modelto the driver as--model <name>; analyst agents default toopus, developers + qa tosonnet; (2) per-agenttimeout_minutes(default 0 = no timeout); supervisor distinguisheserrors.Is(ctx.Err(), context.DeadlineExceeded)→ statuskilled-timeoutfromcontext.Canceled→killed; (3) addedblockedtoKnownStatusesand to the workflow transition matrix (* → blockedfor any agent role;blocked → draftfor 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 byformatEvent()instores/agents.tsinto readable progress lines (assistant text,▸ ToolName path, results); (5) per-run log file at<data_dir>/<project>/runs/<run_id>.log; newGET /api/p/:project/agents/runs/:run_id/logendpoint streams it; AgentsRunsView gained "View full log" button. UI status chip adds amber forkilled-timeout. Spec §4.2, §6.2, §7 updated. -
2026-04-25 — Graph label filters:
Labels []stringadded toGraphNode(populated vialabels_indexjoin);labelsadded toGraphFilter;uniqueLabelscomputed + OR-filter instores/graph.ts; Labels chip group inGraphFilters.vue. Plan: plans/graph-label-priority-filters.md. -
2026-04-27 — Product-owner workflow bypass:
workflow.HasProductOwner()exported helper;CanTransitionshort-circuits totrueandAllowedTargetsreturns the full target set when the user hasproduct-owner;transition.goskips the required-plansGateReadycheck for product-owner. Resolves defectproduct-owner-cannot-transition.md. -
2026-04-26 — Active node pulse visualization:
ACTIVE_STATUS_COLORSadded to graphConstants (in-development=green, in-qa=amber); 3D graph adds a semi-transparent pulsing torus ring viaonEngineTick(sine wave scale 0.85–1.15 at 500 ms period); 2D graph pulses border-width between 2 and 6 px viasetIntervalat 700 ms. Plan: plans/active-node-visualization.md. -
2026-04-26 — Agent status lifecycle:
active_status+done_on_successfields added toAgentConfig;PatchFrontmatterFieldmoved tointernal/artifact(was private inhttp/transition); agent manager sets target artifact status on run start and bundlesdonestatus into the agent's own commit on success; developer agents configuredactive_status: in-development,done_on_success: true; QA configuredactive_status: in-qa. -
2026-04-25 — Markdown editor line wrap toggle:
Compartmentfrom@codemirror/stateused for dynamicEditorView.lineWrappingreconfiguration without recreating the editor;wrapLinesref 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 isdonein 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 keykaos-theme); FOUC-prevention inline script in index.html;App.vuecallstheme.init()on mount; sun/moon icon toggle button in AppHeader;tokens.cssextended 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
ticket→requirementthroughout (artifact.go, config defaults, lifecycle/config.yaml, graphConstants.ts, ArtifactListView typeOptions); removed unused typesepic,plan-dev,release,sprint; restoredtesttype pointing tolifecycle/tests/;ARTIFACT-TYPES.mdreference doc added to repo root;lifecycle/prototypes/Kaos Control/prototype HTML committed. -
2026-04-25 — Priority frontmatter field:
Priority stringadded toFrontmatter;schemaVersionbumped to 2 (auto-rebuild);prioritycolumn + index added toartifactstable;Prioritywired throughFilter/buildWhere/GraphNode/Graph SELECT/upsert; newPriorities()method +GET /prioritiesendpoint;priorityserver-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:
IndexFileand the watcher both computefilepath.Relagainst the symlink-resolved project root (matching the earlier sandbox fix); they refuse to index files whose computed relative path begins with..or is absolute. AddedpruneEscapingPaths()at startup which deletes existing rows fromartifacts,parse_errors,links, andlabels_indexwhose 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-planner→backend-developer,frontend-planner→frontend-developer,developer→test-developer; addedanalystrole; addeddefectartifact type +lifecycle/defects/stage. Six agent configs inlifecycle/config.yaml:requirements-analyst,planning-analyst,backend-developer,frontend-developer,test-developer,qa— each with focused prompt template and scopedallowed_write_paths.internal/config/config.godefaults updated;internal/workflow/workflow.gotransition matrix updated (analyst can self-submit, three developer roles authorised for in-development→in-qa);internal/artifact/artifact.goKnownTypesandstageToType()extended for defects.required_plans.ticketset 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/.gitkeepadded 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 viadefineAsyncComponent), 3D/2D toggle inGraphView.vue, parse-error count badge inAppSidebar.vue(WS-driven refresh onartifact.indexed),GET/PUT /api/p/:project/configbackend 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.vuerewritten to multiplex WS events to agentsStore (agent.) and locksStore (lock.).ArtifactModal.vueandArtifactEditorView.vueextended 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,_destructoron 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.tscomposable (fetches on mount, re-fetches onartifact.indexedWS event),stores/graph.ts(rawNodes/rawEdges, computed filteredNodes/filteredEdges from reactive filter, uniqueTypes/statuses/lineages),api/graph.ts. Router default/p/:projectnow 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, WebSocketartifact.indexedinvalidation),ArtifactEditorView(breadcrumb nav, markdown preview, frontmatter panel, WS live-reload), artifact components (LineageBreadcrumb,FrontmatterPanel,MarkdownPreviewwith markdown-it + wiki-link inline rule →/p/:project/artifacts?lineage=…),useWebSocketcomposable,stores/artifacts(items/filter/detailCache/labels),api/artifacts.ts,api/ws.ts(WsClient with exponential backoff reconnect, singleton per project). Router extended withartifacts/:pathMatch(.*)+editor route. TypeScript clean (vue-tsc --noEmit), Vite build clean (157 modules).
- 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 /transitionwith role-matrix enforcement; rejection child artifact creation;lifecycle/config.yamluser 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),useWebSocketcomposable, 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;ArtifactEditorViewfull 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,manualChunksbundle split
- 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-editorfor local editor launch (spec §16)POST /api/p/:project/agents/:name/preview-prompt(spec §16)
- 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
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).
- 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