Repository navigation
feat!: enforce delegation contract on forge_spawn_subtask - #144
Merged
jflowers merged 2 commits intoOct 26, 2026
Merged
Conversation
- Require task_classification (deterministic or judgment) on every spawn; deterministic subtasks must supply an existence-verified skill reference or a recorded skill_absent_reason (never both); judgment subtasks must omit both - Fail closed with SKILL_REFERENCE_REQUIRED or SKILL_NOT_FOUND on missing references and unresolvable skills - Envelope gains task_classification, resolved skill path, skill_absent_reason, and the tool-rendered skill_directive; forge_subtask_prompt gains an optional skill parameter - Update embedded agent kit, repo-level mirrors, docs/tools.md, CHANGELOG, and OpenSpec change artifacts Fixes unbound-force#143 Assisted-by: glm Generated with AI assistance (glm)
CI MegaLinter enables gosec, which rejects os.WriteFile permissions above 0600 (G306). Both test helpers write fixtures into t.TempDir, so 0600 is equivalent. Assisted-by: glm Generated with AI assistance (glm)
jflowers
force-pushed
the
opsx/spawn-subtask-skill-enforcement
branch
from
October 25, 2026 22:48
22f08c2 to
35e71d3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
forge_spawn_subtasknow enforces the delegation contract established by the parent changeuf_delegate_command(unbound-force/unbound-force#787), closing the silent-omission loophole where subtasks could be spawned with improvised procedures.task_classificationis required (deterministic|judgment) on every spawn; callers omitting it fail closed with a self-describing error naming the parameter and accepted values.skillreference (1-256 chars) or a recordedskill_absent_reason(1-512 chars) — never both. Judgment subtasks must omit both.SKILL_REFERENCE_REQUIRED; unresolvable references fail withSKILL_NOT_FOUND.task_classification, the resolved skill path,skill_absent_reason, and the tool-renderedskill_directive.forge_subtask_promptgains an optionalskillparameter embedding the same fixed directive..opencode/skills/<name>/SKILL.mdthen.opencode/skill/<name>/SKILL.md(first root wins);/-containing refs resolve repo-relative with a post-clean traversal guard; absolute and..-escaping paths are rejected.Agent kit (coordinator agent,
/forgecommand, forge-coordination skill), repo-level mirrors,docs/tools.md, and the CHANGELOG BREAKING entry all ship in the same change. Cross-repo follow-ups: unbound-force/unbound-force#830 (uf.unleash template update), unbound-force/website#329 (website docs).Spec:
openspec/changes/spawn-subtask-skill-enforcement/Fixes #143
How to Test
make check go test ./internal/forge/... ./internal/tools/forge/... -count=1 -race -vAcceptance scenarios live in
openspec/changes/spawn-subtask-skill-enforcement/specs/spawn-delegation-contract/spec.md; every scenario maps to a test. Spot-checks at the MCP boundary (make serve):forge_spawn_subtaskwithouttask_classification→ errortask_classification is required (accepted values: deterministic, judgment).task_classification: "deterministic"with neitherskillnorskill_absent_reason→SKILL_REFERENCE_REQUIRED.task_classification: "deterministic",skill: "forge-coordination"→ready_to_spawnenvelope with resolved path +skill_directive.task_classification: "judgment"withskill→ input-validation error ("valid only for deterministic").skill: "../secret.sh"→ rejected as traversal before any filesystem access.How to Demo
Run the MCP server (
make serve), then as a coordinator agent: attempt a spawn with the old 4-argument shape and observe the self-describing failure; retry withtask_classification+skill: "forge-coordination"and show the envelope's audit fields.Key Files Changed
internal/forge/spawn.goSpawnParams, contract validation,ResolveSkillRef(dual roots + traversal guard),SkillDirective, sentinel errors, stat-error taxonomyinternal/forge/spawn_test.gointernal/tools/forge/tools.gointernal/tools/forge/tools_test.gointernal/agentkit/content/+agentkit_test.go.opencode/commands/forge.md,.opencode/skills/forge-coordination/SKILL.mddocs/tools.mdCHANGELOG.mdopenspec/changes/spawn-subtask-skill-enforcement/Known Issues
The review council (9 reviewers, 2 iterations) returned APPROVE with these acknowledged-but-deferred advisories:
skill_directiveforwarding rule into the judgment bullet (field only exists for deterministic spawns)SpawnSubtaskGoDoc says stat errors are "returned unwrapped" — they are context-wrapped (non-sentinel).opencode/commands/*.mdmirrors (pre-existing; fixed here by manual sync)ProjectPathanchor in boundary test, root-run skip guard, schema type pinsforge_subtask_promptskill verificationThis PR was generated by /uf.finale (AI-assisted).