feat(guardrails): steering channel + shell-in-file rule + capability-gap detector
Bundles three operator-reliability guardrails (Vikunja #28/#29/#30) plus the in-flight branch WIP they were built on top of (reasoning_content capture, operator/project profile editor, write-jail workspaceRoot fix) — the tree is interdependent (SessionOrchestrator references reasoningArtifactId from the WIP) and does not compile as separable subsets, so it lands as one commit. Guardrails: - #28 mid-stage steering: ClientMessage.SteerSession -> GlobalStreamHandler -> orchestrator.submitSteering, reusing SteeringNoteAddedEvent + existing context fold (advisory, non-authoritative; invariants #3/#7). Closes the gap where steering typed off an approval gate was silently dropped. - #29 shell-in-file guardrail: ShellInFileContentRule (core:toolintent) blocks a file_write whose content is a bare shell command (e.g. "mkdir -p ..."); FileWriteTool description now advertises auto-mkdir of parent dirs. Basename-allowlist so the extensionless case is caught; scripts/Makefiles/multiline exempt. - #30 pt1 capability-gap detector: deterministic CapabilityGapDetector maps stage intent -> implied ToolCapability, compares to granted tools, emits advisory CapabilityGapDetectedEvent in FreestyleDriver.lockAndRun. Recorded, never fails the gate and never auto-grants (invariants #3/#4/#5). Reflection rung is pt2. Verified: ./gradlew check green (whole tree).
This commit is contained in:
@@ -18,6 +18,8 @@
|
||||
#
|
||||
# Requires these artifact kinds in ~/.config/correx/config.toml (schemas under docs/schemas/):
|
||||
# [[artifacts]]
|
||||
# id = "discovery"; schema_path = "schemas/discovery.json"; llm_emitted = true
|
||||
# [[artifacts]]
|
||||
# id = "analysis"; schema_path = "schemas/analysis.json"; llm_emitted = true
|
||||
# [[artifacts]]
|
||||
# id = "design"; schema_path = "schemas/design.json"; llm_emitted = true
|
||||
@@ -27,7 +29,18 @@
|
||||
# Prompt files (prompts/*.md, relative to this workflow) must exist for a real run.
|
||||
|
||||
id = "role_pipeline"
|
||||
start = "analyst"
|
||||
start = "discovery"
|
||||
|
||||
# 0. Vet the request against the real repo before any analysis: park on a batched list of
|
||||
# operator questions (underspecification or a claim the code contradicts) or clear it to
|
||||
# proceed. Read-only; owns clarification exclusively so the analyst always emits an analysis.
|
||||
[[stages]]
|
||||
id = "discovery"
|
||||
prompt = "prompts/discovery.md"
|
||||
produces = [{ name = "discovery", kind = "discovery" }]
|
||||
allowed_tools = ["file_read", "list_dir", "shell"]
|
||||
token_budget = 16384
|
||||
max_retries = 2
|
||||
|
||||
# 1. Understand the request and the relevant code. Read-only.
|
||||
# ground_references: every workspace-relative file path the analysis names is checked
|
||||
@@ -93,6 +106,13 @@ max_retries = 2
|
||||
|
||||
# --- forward edges ---
|
||||
|
||||
[[transitions]]
|
||||
id = "discovery-to-analyst"
|
||||
from = "discovery"
|
||||
to = "analyst"
|
||||
condition_type = "artifact_validated"
|
||||
condition_artifact_id = "discovery"
|
||||
|
||||
[[transitions]]
|
||||
id = "analyst-to-architect"
|
||||
from = "analyst"
|
||||
|
||||
Reference in New Issue
Block a user