The schema declared sources as an array of objects, which correx's flat
JsonSchema model can't represent (JsonSchemaProperty has no nested
properties). The strict config loader rejected it at startup, silently
disabling the source_dossier artifact kind. Flatten sources to an array
of strings (URL-prefixed per-source notes), update the gather prompt to
match, and add ResearchSchemaLoadTest to guard every shipped research
schema against the strict loader.
The JSON-schema artifact validator enforced `required` keys but not array
contents, so a model could emit `"requirements": []` (or `"steps": []`,
`"components": []`) and pass — a misread brief sailing through as a valid
artifact. That empty-list gap is the hole under role-reliability §3's
keystone ("force the analyst to define done; verifiability propagates
downstream").
- JsonSchemaProperty gains `minItems` and `items` (recursive), letting a
schema declare a required-non-empty / element-typed array.
- JsonSchemaValidator checks array length >= minItems and each element's
declared item type (with index in the message). Pure + deterministic;
single-return to stay within the detekt ReturnCount budget.
- Role-pipeline schemas tightened: analysis.requirements, design.components,
impl_plan.steps → minItems 1 + items string. Prompt-free — these arrays
were already `required`; this only forbids them being empty.
§4 (force a non-empty `alternativesConsidered` on the architect) is
deliberately NOT taken: it contradicts the standing "don't raise
alternatives unnecessarily" preference the architect prompt already encodes.
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.
Users declare artifact kinds in [[artifacts]] (id + schema_path to a JSON-schema file + llm_emitted); ConfigArtifactKind registers them at startup via createWorkflowLoader(extraKinds). New JsonSchemaValidator validates any non-built-in kind generically against its declared deriveJsonSchema(), so an LLM-emitted custom kind is checked against its shape, never trusted. Removed the dead payloadSerializer from the ArtifactKind contract. Schema source is path-to-file (not inline TOML — the hand-rolled parser can't nest).
DefaultMaterializingArtifactWriter and its MaterializingArtifactWriter interface
were test-only orphans after the sandbox dual-write retirement (95e79bf) - no
production wiring, never instantiated outside their own test. Delete all three.
Add healthcheck_*.txt to .gitignore (one-off generated output).
P2-1: Guard SessionId NPE with safe-call in SessionsReducer
P2-2: Remove 16 dead event classes + reducer branches; replace with live orchestration events in tests
P2-3: Standardize ChatInput divergences — guard CancelSession, single-arg ProtocolError
P2-4: Remove dead TuiToolRecord.diff and ToolDisplayStatus.REQUESTED
P2-5: Remove dead streamLive from SessionEventBridge
P2-6: Extract SessionsReducerContext data class for 6+ param method
P2-7: Replace StageId("none") sentinel with TypeId.NONE
P2-9: Add TypeId.random() factory on all type-alias IDs
P2-10: Add KDoc on schemaVersion documenting reserved-for-migration
P2-8: Verified RouterReducer string-template already correct (TypeId value class toString)
- Add field to ProcessResultArtifact ("success"/"failed")
- Materialize ToolResult.Failure as a failed ProcessResult artifact
in the orchestrator, with artifact lifecycle events emitted
- Non-recoverable ToolResult.Failure now fails the stage ("FATAL:")
instead of being silently fed back as LLM context
- Fix enterStage to return Continue on non-retryable failures,
enabling back-edge transitions (fix/retry loops)
- Add ArtifactFieldEquals transition condition with flat and
nested dot-notation field access + FieldOperator enum
(eq, neq, gt, gte, lt, lte)
- Wire artifact_field_equals through ConditionFactory and
TomlWorkflowLoader (condition_field, condition_operator)
- Cache ProcessResult artifact content for evaluation context
- Add 16 tests covering all condition operators and error cases
- DefaultTransitionResolver catches condition evaluation errors
and returns Blocked instead of crashing
Implements the full conversational router facade: RouterState, RouterReducer,
RouterProjector, RouterRepository, RouterContextBuilder, RouterFacade, protocol
types, WebSocket wiring, infrastructure factory, and deterministic test suite.
Also fixes spec divergences found in post-implementation review:
- Add SteeringNote domain object to core:context (epic prerequisite)
- Rename RouterFacade.handleChat → onUserInput per spec interface contract
- Add in-memory ConcurrentHashMap conversation history to DefaultRouterFacade
- Make RouterRepository.getRouterState suspend
- Rename RouterConfig.keepLast → conversationKeepLast, fix defaults (6, 4096)
- Refactor InfrastructureModule.createRouterFacade to self-assemble internally
- Fix FileReadTool: allowedPaths was dead constructor param (@SuppressUnusedParameter);
now stored as private val and enforced in validateRequest
- Disable koverVerify on modules tested via testing/ submodules or with
hardware/integration dependencies (24 modules); build gate now passes clean
Deletes 21 Module.kt scaffolding objects that were never wired into any DI
registry. Removes unused imports across 8 production and 3 test files.
Restores StageExecutor, CyclePolicyResolver, PolicyValidation, and
ReplayContractTest — these are deferred features, not dead code.