feat(toolintent): stage write-manifest enforcement (role-reliability §2)
Scope creep — an implementer writing files it never declared — is the dominant local-model failure that compiler/tests don't catch. A stage can now declare a `writes` manifest (workspace-relative globs); a FILE_WRITE that resolves inside the workspace but outside the declared set is raised as PATH_OUTSIDE_MANIFEST → BLOCK, so the model gets the violation as tool feedback and retries within scope (the §2 bounded-retry signal). Implemented as a plane-2 rule beside PathContainmentRule (same effect-based dispatch on FILE_WRITE, same symlink-safe WorldProbe resolution, same recorded observations so replay reads them back — invariant #9). An empty manifest is unrestricted; out-of-workspace targets stay PathContainmentRule's concern, so the two rules don't double-flag. - StageConfig.writeManifest + TomlWorkflowLoader `writes` parsing - ToolCallAssessmentInput.writeManifest, threaded from the active stage via SessionOrchestrator.runPlane2Assessment - new ManifestContainmentRule, registered in the server assessor - shared candidatePathStrings extracted so both path rules judge the same target set - role_pipeline.toml documents the option on the implementer stage The dedicated ManifestViolationEvent the spec names is not added: the violation is already recorded replayably in ToolCallAssessedEvent (issue + observations + BLOCK) and surfaced in the TUI rationale band. A first-class event is worth adding only once reconciliation consumes it.
This commit is contained in:
+3
@@ -638,6 +638,7 @@ abstract class SessionOrchestrator(
|
||||
}
|
||||
val plane2Risk: RiskSummary? = runPlane2Assessment(
|
||||
sessionId, stageId, invocationId, toolCall.function.name, request, tool, effectives,
|
||||
stageConfig.writeManifest,
|
||||
)?.let { assessment ->
|
||||
if (assessment.recommendedAction == RiskAction.BLOCK) {
|
||||
emit(
|
||||
@@ -893,6 +894,7 @@ abstract class SessionOrchestrator(
|
||||
request: ToolRequest,
|
||||
tool: Tool?,
|
||||
effectives: RunEffectives,
|
||||
writeManifest: List<String>,
|
||||
): RiskSummary? {
|
||||
val assessor = toolCallAssessor ?: return null
|
||||
val policy = effectives.policy ?: return null
|
||||
@@ -903,6 +905,7 @@ abstract class SessionOrchestrator(
|
||||
workspace = policy,
|
||||
probe = worldProbe,
|
||||
paramRoles = tool?.paramRoles ?: emptyMap(),
|
||||
writeManifest = writeManifest,
|
||||
),
|
||||
)
|
||||
emit(
|
||||
|
||||
Reference in New Issue
Block a user