Commit Graph

254 Commits

Author SHA1 Message Date
kami 8bf56604c7 chore(examples): root-level workflow descriptions + curated .correx project profile 2026-06-11 21:47:39 +04:00
kami f521ac89e5 feat(memory): markdown heading extraction in RepoMapIndexer
h1-h3 headings become the file's symbols, so design docs and GDDs are
embedded and retrievable alongside code. .md files now also participate in
workspace fingerprinting (INDEXED_EXTENSIONS derives from pattern keys).
2026-06-11 14:39:05 +04:00
kami 81a21bfe57 feat(memory): GDScript symbol extraction in RepoMapIndexer
Adds 'gd' to SYMBOL_PATTERNS (func/class_name/class/signal/enum at column 0,
optional static prefix). Because INDEXED_EXTENSIONS derives from the pattern
keys, .gd files now also participate in WorkspaceStateProbe fingerprinting,
making fp: state keys content-sensitive for Godot repos.
2026-06-11 14:32:04 +04:00
kami 1976d8ad2b test(replay,integration): repo-knowledge replay derivation + state-keyed scan reuse
Adds two test files to prove B4/B3 invariants end-to-end:

- RepoKnowledgeReplayTest (testing/replay): projection-based replay tests that
  verify recorded RepoKnowledgeRetrievedEvent drives relevantFiles context assembly
  without consulting a retriever (test A), and that a log without the retrieval event
  falls back to the RepoMapComputedEvent legacy path (test B).

- RepoMapReuseIntegrationTest (testing/integration): end-to-end test using the real
  ProjectMemoryService + L3RepoKnowledgeRetriever + InMemoryL3MemoryStore proving
  exactly one index walk across two sessions sharing the same stateKey, and that the
  second session receives non-empty repo hits via the shared L3 embeddings despite
  having no per-session RepoMapComputedEvent.

New test deps (noted per constraint):
- testing/replay: adds core:context (needed to import ContextLayer/ContextEntry from
  the buildRelevantFilesEntry return type).
- testing/integration: adds apps:server (ProjectMemoryService, L3RepoKnowledgeRetriever,
  FakeWorkspaceStateProbe), core:router (InMemoryL3MemoryStore), core:config (ProjectConfig).
  No new inter-core or upward production dependencies introduced.
2026-06-11 13:52:06 +04:00
kami 7a9e060a55 feat(kernel): RepoKnowledgeRetriever port + relevance-retrieved repo context with recency fallback 2026-06-11 13:39:57 +04:00
kami 9d38a89c88 feat(memory): state-keyed repo-map reuse via L3 presence check; embed entries on fresh scan
- Add existsByTurnIdPrefix(prefix) to L3MemoryStore interface; implemented in
  InMemoryL3MemoryStore (mutex-guarded) and TurboVecL3MemoryStore (metadata map).
  All test fakes (TrackingL3MemoryStore, CapturingStore) updated.
- Introduce RepoMapIndexerPort interface; RepoMapIndexer implements it. Allows
  injection of test doubles without framework overhead.
- ProjectMemoryService.indexAndRecord: reads latest WorkspaceStateObservedEvent
  from event store (invariant #9 — recorded fact, not re-probe); skips scan+embed
  when L3 already holds entries for "repomap:<root>:<stateKey>"; passes stateKey
  to RepoMapComputedEvent; embeds each scanned entry after append; per-entry
  failures warn-logged (CancellationException rethrown).
- Tests: InMemoryL3MemoryStoreTest +2 cases; new ProjectMemoryServiceReuseTest
  covers same-key skip, changed-key re-index, no-observation always-reindex,
  and embedding turnId tag verification.
2026-06-11 13:30:57 +04:00
kami 4dbe637f4a feat(memory): WorkspaceStateProbe (git or fingerprint) + observeAndRecord at session start 2026-06-11 13:22:50 +04:00
kami f1fc43cc85 feat(router,server): wire workflow inventory and project profile into router context per turn 2026-06-11 13:15:32 +04:00
kami 823c2e0ade feat(events): WorkspaceStateObservedEvent, RepoKnowledgeRetrievedEvent, stateKey on RepoMapComputedEvent 2026-06-11 13:11:12 +04:00
kami 72e7002623 feat(router): replace bare refusal with triage directive in system prompt 2026-06-11 11:15:59 +04:00
kami a32e9acac4 feat(router): inject available-workflows and project-profile into router L0 context 2026-06-11 11:11:04 +04:00
kami ac458d83e5 feat(workflow): root description field in workflow TOML surfaced through registry 2026-06-11 11:08:33 +04:00
kami ed0dca8b78 feat(kernel,artifacts): inject artifact-kind vocabulary into architect context 2026-06-11 10:06:40 +04:00
kami beed501d60 feat(kernel): label reviewer artifacts as critic feedback on back-edge re-entry 2026-06-10 21:56:05 +04:00
kami 629b910e7f feat(kernel): retried stages receive explicit failure feedback; default retry backoff 1s 2026-06-10 21:53:45 +04:00
kami 91cca9aee3 feat(server,kernel): bind ProjectProfile per session and inject as L0 context 2026-06-10 21:49:31 +04:00
kami 4389163c5c feat(events,sessions): ProjectProfileBoundEvent reduced into session state 2026-06-10 21:47:30 +04:00
kami 5eda64949f feat(config): ProjectProfile — per-repo standing context at .correx/project.toml 2026-06-10 21:45:46 +04:00
kami 0de02bb3a7 feat(server,kernel): post-plan approval gate — operator reviews plan before lock
After a freestyle plan compiles successfully, requestPlanApproval is invoked
before ExecutionPlanLockedEvent is emitted. Rejection emits
ExecutionPlanRejectedEvent(source="operator") with no lock and no phase-2 run.
Adds public SessionOrchestrator.requestPlanApproval seam delegating to
requestStageApproval, wired in Main.kt. Two new tests cover the reject and
approve paths; existing tests stay green via the default passthrough param.
2026-06-10 21:36:17 +04:00
kami 3d43f34d3b fix(narration): use plan_rejected kind and include source in instruction 2026-06-10 21:31:34 +04:00
kami 04836b751c feat(server): narrate execution-plan rejection 2026-06-10 21:30:07 +04:00
kami 0553b1b6ee feat(events,server): ExecutionPlanRejectedEvent — freestyle compile failure visible in log
Both silent-failure branches in FreestyleDriver.lockAndRun now emit
ExecutionPlanRejectedEvent (source=compile or missing_content) before
returning, satisfying invariant #1. Event registered in eventModule for
correct polymorphic serialization.
2026-06-10 21:26:18 +04:00
kami fba2bbb17e fix(kernel): llm-emitted artifacts require stored content — close phantom path
emitLlmArtifacts previously emitted ArtifactCreated/Validating/Validated for every
llm-emitted slot unconditionally, fabricating "validated" artifacts when the model
produced no content. Gate now mirrors the F-015 file_written check: slots with null
or blank cached content are skipped with a warn log. Regression test added in
NeedsArtifactInjectionTest verifies no artifact events appear for a blank-response stage.
2026-06-10 21:20:28 +04:00
kami 113f403f5a chore(examples): add qa_ping smoke workflow; ignore tui-go binary and qa/ scratch
qa_ping is a minimal single-stage file_write workflow used for live QA smoke
runs (writes 'pong' into the qa/ scratch repo). The qa/ workspace itself is a
nested git repo and stays untracked; the compiled tui-go binary is ignored.
2026-06-10 20:54:59 +04:00
kami 3ad42ff29e fix(llama-cpp): raise default request timeout 60s -> 600s
Long generations (plan/patch stages on larger contexts) exceeded the 1-minute
HTTP timeout and surfaced as provider failures mid-pipeline.
2026-06-10 20:54:39 +04:00
kami c82a86477d fix(tui): wrap long artifact lines in viewer instead of truncating
Artifact content wider than the modal was silently cut off. Lines are now
hard-wrapped at rune boundaries to the modal content width, and scroll
clamping uses the wrapped line count so the last page stays reachable.
2026-06-10 20:54:39 +04:00
kami cba9dd4583 fix(server): artifact viewer showed FileWrittenArtifact envelope, not file content
file_written artifacts store a JSON envelope ({path, contentHash, size, mode})
whose contentHash points at the real bytes in CAS. The viewer rendered the
envelope itself. StreamQueries now detects the envelope shape (string "path" +
"contentHash") and dereferences the inner hash, falling back to the raw string
when parsing fails or the inner hash is missing (WARN logged — likely evicted).
2026-06-10 20:54:29 +04:00
kami 883e23dec7 feat(router,inference,config): pin narration to a configured model_id
[router.narration] model_id selects which provider handles narration turns
instead of generic capability routing — lets a small/fast model own narration
while the main model keeps CHAT/STEERING.

- NarrationSettings.modelId parsed from TOML, threaded via RouterConfig
  .narrationModelId into DefaultRouterFacade.narrate (3-arg route)
- DefaultInferenceRouter: exact-id match against healthy providers, with a
  post-select health check; any miss logs WARN and falls back to capability
  routing (never fails the narration turn)
- onUserInput unchanged — only narrate uses the pinned model (tested)
2026-06-10 20:53:28 +04:00
kami cc6b402a23 fix(server,kernel,events): harden artifact read + restore workspace on reopen + cleanups
Follow-ups spotted while fixing the artifact-content bug:

- #1 robustness: listArtifacts resolves content via runCatching, degrading to null
  instead of failing the whole listing on one bad CAS read (resolveContent helper)
- #2 eviction visibility: WARN when a referenced artifact hash resolves to null
  (most likely evicted/compacted while still referenced by the event log)
- #3 ordering trap: document on ArtifactCreated/ArtifactContentStored that 'Created'
  fires at validation, AFTER ContentStored at inference — the latent fold hazard
- #4 reopen gap: SessionSnapshot now carries workspaceRoot (derived from
  SessionWorkspaceBound), so a reopened session restores its workspace label
- #5 orchestrator headroom: isBackEdge externalised to a top-level function so
  DefaultSessionOrchestrator drops below the TooManyFunctions threshold (was at it)

#6 (orphaned empty F-010-era artifacts) needs no code change — empty content is
already normalised to null ('(no content stored)'); emission is prevented going
forward by the shipped F-010/F-018/F-020 fixes.
2026-06-10 12:58:28 +04:00
kami 5f7b6f1f18 fix(server): artifact viewer dropped content hash due to event ordering
Root cause of empty artifact content (even for role_pipeline sessions with intact CAS
data): ArtifactContentStored is emitted at inference time, BEFORE ArtifactCreated (which
fires at validation). listArtifacts only applied the content hash via accs[id]?.let{} —
a no-op because the artifact wasn't in the map yet — so the hash was silently dropped and
every artifact showed blank.

Verified against the live store (session 4679ed1e): all 5 artifacts had hash=none before,
now resolve to their validated content (analysis 867B, design 1074B, impl_plan 966B,
patch 574B, review_report 369B).

- ArtifactContentStored now seeds the accumulator (getOrPut), order-independent
- fold extracted to pure StreamQueries.planArtifacts for unit testing
- regression test: content hash captured when ContentStored precedes Created
- inference-output fallback retained for genuinely content-less stages
2026-06-10 12:49:11 +04:00
kami 90ba7244d5 fix(server): artifact viewer falls back to raw stage output when no content hash
Artifacts with no ArtifactContentStored mapping (older sessions, or stages where the
slot->hash link was lost) showed a blank pane. listArtifacts now falls back to the
stage's last InferenceCompleted output so the operator can still see what the stage
produced. Empty-string content is normalised to null so the viewer shows a clear
'(no content stored)' instead of a blank pane.

Verified: role_pipeline sessions already resolve content from CAS (index + segments
intact, store rooted at ~/.config/correx/artifacts); the previously-blank sessions are
healthcheck/degenerate runs whose inference output was genuinely empty.
2026-06-10 12:35:07 +04:00
kami 3c5c2999d3 fix(server,tui): restore output panel + full event history on session reopen
Reopening a session after relaunch sent only a thin summary snapshot, so the output
panel was blank and the event list showed at most 7 entries (of a 100+ event run).

- SessionSnapshot now carries lastOutput/lastResponse, resolved from the last
  InferenceCompleted artifact in CAS; tui-go onSnapshot restores the output panel
- recentEvents cap raised 7 -> 200 so a normal session's event list isn't truncated
- resolveLastOutput extracted as a helper

Note: artifact content is unaffected — verified intact and CAS-retrievable for
role_pipeline sessions (artread); content-less artifacts occur only in workflows that
record no ArtifactContentStored (e.g. healthcheck/process-result stages).
2026-06-10 12:22:54 +04:00
kami bb70c94a99 feat(kernel,events): freestyle graph re-routing — deterministic override core
Implements the replay-safe half of preemptive redirect (graph re-routing). An
operator-confirmed PreemptRedirectEvent overrides the resolver's edge at the next
transition boundary; the pure resolver stays untouched.

- PreemptRedirectEvent / PreemptRedirectBlockedEvent (+ serialization registration)
- TransitionExecutedEvent + TransitionDecision.Move gain optional redirectId — the
  durable 'consumed once' marker
- PreemptRedirect.decide: pure decision over the event log (override / block / none),
  so replay reaches the identical outcome without re-classifying (invariant #8)
- override wired in DefaultSessionOrchestrator.step above resolveTransition; back-edge
  jumps count against the existing maxRetries cap via executeMove
- blocks jumps to unknown stages or targets with unsatisfied needs
- DomainEventMapper: redirect events mapped to null (operator surface ships with the
  LLM-proposal + approval-confirm front-half)
- tests: override+consume-once, block-on-needs, block-on-unknown, ignore-consumed

Front-half (LLM proposal -> approval-gate confirm -> emit PreemptRedirectEvent) is
deferred; needs live LLM verification. Until it ships no redirect event is emitted in
production, so the override is inert. Spec: docs/plans/2026-06-10-freestyle-graph-rerouting.md
2026-06-10 11:51:47 +04:00
kami bc83a2d64e docs(qa): mark F-002/F-003/F-004/F-006/F-016 fixed in audit report 2026-06-10 11:14:09 +04:00
kami 8c9ca9db00 fix(tui): F-006 flatten multi-line paste in input render
A multi-line paste put raw newlines into the single-line input slot, overflowing
the fixed-height box and leaving a smeared/stale region. flattenForDisplay collapses
newlines (↵) and tabs for rendering only; the inputBuffer keeps real characters for
submission.
2026-06-10 11:13:45 +04:00
kami ee24b7786a fix(kernel,server,tui,inference): generic resource gauge + F-002/F-003/F-004
- generic SystemResourceProbe: owner-agnostic /proc/meminfo system RAM
  overlaid on the vendor GPU probe, wired on both managed and static paths
  so the VRAM/GPU/RAM gauge renders with an external llama-server (was dormant)
- F-002: classify provider 4xx (e.g. llama.cpp 400) as terminal, not retryable
  (except 408/429); stops retrying deterministic request failures to exhaustion
- F-003: FileSystemPromptLoader expands leading ~ / ~/ to user home
- F-004: map InferenceFailedEvent + RetryAttemptedEvent to new
  ServerMessage.InferenceFailed / RetryAttempted, decoded+rendered in tui-go;
  ArtifactContentStoredEvent explicitly mapped to null (internal, no warn)
- tests: tilde expansion, SystemResourceProbe (static/managed/fail-soft)
2026-06-10 11:07:08 +04:00
kami a92b5a3531 feat(tui,server,config): live config editor + artifact viewer
Two operator features over the existing WebSocket protocol, plus a tui-go nav fix.

Config editor (g / palette "config"):
- core:config gains a thin registry stack: OrchestrationKnobs ([orchestration]
  block), CorrexConfigWriter (round-trip TOML serializer; parseToml(write(c))==c),
  ConfigHolder (AtomicReference live config), and EditableConfig (allowlist of
  editable fields + patch applier; security fields are absent so they can never
  be patched).
- ConfigService validates -> persists (atomic temp+move) -> swaps the holder ->
  rebuilds config-derived services. Live-apply is scoped to safe seams: stage
  timeout, compaction threshold (now a supplier), router knobs (routerFacade
  rebuild), and personalization/project toggles all take effect on the next
  session/turn; in-flight sessions keep the config they started with.
  server.host/port are persisted + badged "restart required", not hot-swapped.
- Protocol: GetConfig / UpdateConfig / ConfigSnapshot; TUI overlay with
  type-aware editing (bool toggle, enum cycle, inline int/string edit) and save.

Artifact viewer (v / palette "artifacts"):
- Server assembles a session's artifacts from the event log and resolves bytes
  from the CAS store (StreamQueries.listArtifacts) — a replay-neutral snapshot
  read. TUI overlay lists artifacts and shows scrollable content.

tui-go fix: workflow failure no longer clears selectedID (which yanked the user
to the list); listNav lands on the first session when nothing is selected
instead of wrapping to a random one.

Config is not event-sourced (it lives in TOML); editing stays outside the log.
2026-06-09 10:18:35 +04:00
kami 89487db72a fix(kernel,router): rehydrate by CAS content hash (F-021) + narration budget
rehydrate() now scans ArtifactContentStoredEvent (the durable slot->CAS-hash
bridge) and fetches stored bytes by content hash, keying the cache by the
logical slot name. The slot name is never passed to the hash-keyed CAS, so the
odd-length-hex crash is gone and artifact content is restored on cold-start
resume. RehydrateTest reworked to use a content-addressed fake store (slot name
!= content hash) so it actually exercises the bug. Replay (#8) is unaffected —
rehydrate is live-resume-only.

Also bump router narration maxTokens 1024 -> 4096: reasoning models were
spending the whole budget thinking and emitting empty content (finishReason=
length), so narrations never reached the TUI.

Removes the stale TUI-refactor progress doc.
2026-06-09 10:14:04 +04:00
kami b407b47503 fix(kernel,tools,validation): unblock role_pipeline end-to-end + QA audit
Live QA audit of role_pipeline against a sample repo surfaced and fixed a
chain of defects that prevented any tool+artifact workflow from running
end-to-end. The pipeline now completes cleanly with a real, validated,
reviewed file change.

- F-008 workflow: propagate stage token_budget -> generationConfig.maxTokens
  (TomlWorkflowLoader) so large stages stop truncating at the 2048 default.
- F-009 tools/kernel: file_read missing-file is recoverable; recoverable tool
  errors feed back into the loop instead of aborting the stage.
- F-010/F-018 kernel: reject premature stage_complete and nudge the model to
  emit the owed artifact / invoke a write tool (bounded by MAX_TOOL_ROUNDS).
- F-011 schemas: list-typed artifact fields string -> array (analysis/design/
  impl_plan) to match model output.
- F-012 kernel: strip an outer markdown code fence from LLM artifacts.
- F-013 validation: payload-validation failures are retryable; structural
  failures stay terminal (wires the invariant-#7 reject+retry contract).
- F-014 tools: file_edit schema matches its real params; edits emit a diff.
- F-015 kernel: record tool-execution events + materialise a real file_written
  artifact from the on-disk write; gate validation so a no-write stage cannot
  be rubber-stamped (no more false-success runs).
- F-016 server: raise stageTimeoutMs 60s -> 180s for slow local models.
- F-019 artifacts/kernel: file_written artifact carries the unified diff so the
  reviewer can verify the change (shared DiffUtil; file_write emits a diff too).
- F-020 tools: model-correctable file_edit failures are recoverable + steer
  toward replace/file_write over the fragile patch op.

Full findings register (F-001..F-021, incl. open F-021 resume rehydrate bug)
in qa/audit-report-2026-06-08.md. All module tests green.
2026-06-08 21:51:21 +04:00
kami d518400b5f fix(validation): resolve F-007 artifact validator CAS-hash conflation
ArtifactPayloadValidator passed the logical slot name into the
content-hash-keyed CAS store, crashing every workflow with a typed
produces slot. Validator now reads payloads from a content map threaded
through ValidationContext (populated from the orchestrator's per-session
artifact cache across all stages) and no longer depends on ArtifactStore.

Also records the slot->CAS-hash mapping as a new ArtifactContentStoredEvent
(registered for serialization), emitted at both CAS write sites, so
artifact content is recoverable from CAS by hash after a cold start.
2026-06-08 17:26:34 +04:00
kami 3f59faaa08 fix(inference,tools): unblock llama.cpp tool stages + workspace-relative file_read
Two fixes surfaced by the 2026-06-08 QA audit running role_pipeline on a local
llama-server.

F-001: LlamaCppInferenceProvider sent both a GBNF grammar and tools on the same
request; llama.cpp rejects the combination ("Cannot use custom grammar
constraints with tools"), failing every stage that has tools and produces a
schema-constrained artifact. Drop the grammar when tools are present and rely on
post-hoc schema validation + retry (invariant #7 still holds). TODO left for a
first-class emit_artifact tool as the proper fix.

F-005: FileReadTool resolved relative paths via toAbsolutePath() against the JVM
process cwd instead of the bound workspace, so the file-read jail anchored to the
wrong root and disagreed with the Plane-2 PATH_CONTAINMENT check. Add workingDir
to FileReadConfig, wire workspace.workingDir in the per-workspace tool builder,
and give FileReadTool a resolvePath() mirroring FileWriteTool/FileEditTool.
2026-06-08 16:37:20 +04:00
kami 54a54f4760 feat(server): reinstate JournalCompactionService with real inference 2026-06-08 11:02:14 +04:00
kami 5c03d1a772 feat(server): wire real inference into ProfileAdaptationService 2026-06-08 10:59:23 +04:00
kami 90d76e7bd1 feat(personalization): ProfileAdaptationService — propose-only learned adaptation 2026-06-08 10:42:19 +04:00
kami 0c7fa70f8d feat(personalization): OperatorProfileBoundEvent + bind-at-start + inject about context
Records operator profile as a snapshot event at session start (invariants #8/#9),
populates SessionState.boundProfile via reducer, and injects the about text as a
pinned L0 SYSTEM context entry in every stage. Gated by PersonalizationConfig.enabled.
2026-06-08 10:38:47 +04:00
kami 76b19e2f63 feat(config): OperatorProfile + ProfileLoader + PersonalizationConfig 2026-06-08 10:34:41 +04:00
kami 7af5d602bc fix(kernel,server): remove stub compaction + log missing summary artifact 2026-06-08 10:22:16 +04:00
kami 2b1faeaadd fix(kernel,server): wire JournalCompactionService in production + fix SessionSummaryProjector import 2026-06-08 10:20:30 +04:00
kami 7cec168e86 feat(server): real GET /sessions via SessionSummaryProjector 2026-06-08 10:17:20 +04:00
kami 831ba003e0 feat(sessions): SessionSummary + SessionSummaryProjector 2026-06-08 10:15:56 +04:00