Skip to content

Commit df2ae0c

Browse files
workflow: Allow non-SCE helpers in SCE workflows
SCE workflows now scope handoff prohibitions to SCE skills while allowing relevant non-SCE helpers to return control to the active step without changing workflow invariants. OpenCode permissions and generated-output contracts enforce the new orchestration rules. Co-authored-by: SCE <sce@crocoder.dev>
1 parent c7e4755 commit df2ae0c

30 files changed

Lines changed: 455 additions & 62 deletions

config/pkl/base/workflow-brownfield.pkl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ local titleAndPurpose = model.semanticReference.apply(
3939
4040
Own this workflow from input parsing through its terminal user-visible response.
4141
Execute the phases below directly and in order. Phase statuses are internal state,
42-
not inter-skill handoffs. Do not invoke another SCE skill, sibling package, or
43-
workflow command. Follow the canonical workflow's steps, gates,
42+
not inter-SCE workflow handoffs. Do not invoke another SCE skill, sibling SCE
43+
package, or SCE workflow command. Follow the canonical workflow's steps, gates,
4444
and stops exactly as written: never invent, skip, reorder, or merge a step.
4545
4646
\(scopeStatement)
@@ -58,6 +58,8 @@ local titleAndPurpose = model.semanticReference.apply(
5858
canonical workflow says to continue. Stop only at a user wait or terminal branch.
5959
Clarification and bootstrap waits resume this same skill in the same session.
6060
Never expose an internal phase result as the workflow's final response.
61+
62+
\(model.helperSkillCompositionRule)
6163
""" + "\n\n",
6264
scopeStatement + "\n\n"
6365
)
@@ -356,7 +358,7 @@ local renderSkillBody = (mode: model.WorkflowRenderMode) -> """
356358
- Never write a fact scoring below `50` as truth.
357359
- Never resolve a material contradiction without disclosing it.
358360
- Never write a confidence score, hash, timestamp, or date under `context/`.
359-
- Never invoke another skill, sibling package, or workflow command.
361+
- Never invoke another SCE skill, sibling SCE package, or SCE workflow command.
360362
- Never synchronize context, validate a plan, select or execute a task, or create
361363
a Git commit.
362364
- Never treat this workflow as recurring context maintenance.

config/pkl/base/workflow-change-to-plan.pkl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ changeToPlanSkillBody = """
77
88
Own this workflow from input parsing through its terminal user-visible response.
99
Execute the phases below directly and in order. Phase statuses are internal state,
10-
not inter-skill handoffs. Do not invoke another SCE skill, sibling package, or
11-
workflow command. Follow the canonical workflow's steps, gates,
10+
not inter-SCE workflow handoffs. Do not invoke another SCE skill, sibling SCE
11+
package, or SCE workflow command. Follow the canonical workflow's steps, gates,
1212
and stops exactly as written: never invent, skip, reorder, or merge a step.
1313
1414
## Phase references
@@ -45,6 +45,8 @@ Approval, clarification, revision, failed-validation repair, and bootstrap waits
4545
resume this same skill in the same session. Never expose an internal phase result
4646
as the workflow's final response.
4747
48+
\(model.helperSkillCompositionRule)
49+
4850
## Input
4951
5052
`$ARGUMENTS` is the change request, in free-form prose.

config/pkl/base/workflow-commit.pkl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -708,8 +708,8 @@ local commitSkillBody = """
708708
709709
Own this workflow from input parsing through its terminal user-visible response.
710710
Execute the phases below directly and in order. Phase statuses are internal state,
711-
not inter-skill handoffs. Do not invoke another SCE skill, sibling package, or
712-
workflow command. Follow the canonical workflow's steps, gates,
711+
not inter-SCE workflow handoffs. Do not invoke another SCE skill, sibling SCE
712+
package, or SCE workflow command. Follow the canonical workflow's steps, gates,
713713
and stops exactly as written: never invent, skip, reorder, or merge a step.
714714
715715
## Phase reference
@@ -738,6 +738,8 @@ Approval, clarification, revision, failed-validation repair, and bootstrap waits
738738
resume this same skill in the same session. Never expose an internal phase result
739739
as the workflow's final response.
740740
741+
\(model.helperSkillCompositionRule)
742+
741743
## Input
742744
743745
`$ARGUMENTS` is optional. Split it into two parts before invoking the skill:

config/pkl/base/workflow-content.pkl

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,15 @@ hidden commandBanner = (workflowName: String) -> packageOnlyBlock.apply("\(workf
169169
hidden inlinePhaseBody = (phaseBody: WorkflowBody) ->
170170
compositeOnlyBlock.apply(phaseBody.render.apply("composite"))
171171

172+
/// Non-SCE skills may help with work inside the active workflow step without
173+
/// becoming an alternate owner of the workflow's control flow.
174+
helperSkillCompositionRule = """
175+
Relevant non-SCE skills may be used as helper capabilities during the active step.
176+
They are not workflow handoffs: when a helper returns, control returns to the active
177+
step. Helper use must preserve the canonical phase order, gates, waits, writes,
178+
validation, stops, and terminal user-visible output.
179+
"""
180+
172181
/// Canonical composite workflow bodies and package-local references for the
173182
/// four phase-based workflows. Target renderers add only supported entrypoint
174183
/// frontmatter; all operational and persisted-document content remains
@@ -180,9 +189,9 @@ nextTaskSkillBody = """
180189
181190
Own this workflow from input parsing through its terminal user-visible response.
182191
Execute the phases below directly and in order. Phase statuses are internal state,
183-
not inter-skill handoffs. Do not invoke another SCE skill, sibling package, or
184-
workflow command except `sce-decision`, and invoke `sce-decision` only from the
185-
successful context-synchronization decision gate. Follow the canonical workflow's steps, gates,
192+
not inter-SCE workflow handoffs. Do not invoke another SCE skill, sibling SCE
193+
package, or SCE workflow command except `sce-decision`, and invoke `sce-decision`
194+
only from the successful context-synchronization decision gate. Follow the canonical workflow's steps, gates,
186195
and stops exactly as written: never invent, skip, reorder, or merge a step.
187196
188197
## Phase references
@@ -219,6 +228,8 @@ Approval, clarification, revision, failed-validation repair, and bootstrap waits
219228
resume this same skill in the same session. Never expose an internal phase result
220229
as the workflow's final response.
221230
231+
\(helperSkillCompositionRule)
232+
222233
## Input
223234
224235
Parse `$ARGUMENTS` into three positional parts before invoking any phase:
@@ -342,9 +353,9 @@ validateSkillBody = """
342353
343354
Own this workflow from input parsing through its terminal user-visible response.
344355
Execute the phases below directly and in order. Phase statuses are internal state,
345-
not inter-skill handoffs. Do not invoke another SCE skill, sibling package, or
346-
workflow command except `sce-decision`, and invoke `sce-decision` only from the
347-
successful context-synchronization decision gate. Follow the canonical workflow's steps, gates,
356+
not inter-SCE workflow handoffs. Do not invoke another SCE skill, sibling SCE
357+
package, or SCE workflow command except `sce-decision`, and invoke `sce-decision`
358+
only from the successful context-synchronization decision gate. Follow the canonical workflow's steps, gates,
348359
and stops exactly as written: never invent, skip, reorder, or merge a step.
349360
350361
## Phase references
@@ -381,6 +392,8 @@ Approval, clarification, revision, failed-validation repair, and bootstrap waits
381392
resume this same skill in the same session. Never expose an internal phase result
382393
as the workflow's final response.
383394
395+
\(helperSkillCompositionRule)
396+
384397
## Input
385398
386399
`$ARGUMENTS` is the plan name or plan path.

config/pkl/base/workflow-context-sync.pkl

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,9 @@ local taskRoleData = new SyncRole {
416416
- Create the context root. `sce setup --bootstrap-context` owns that.
417417
- Narrate changed files as documentation. Feature existence is the only reason
418418
to document a change that introduced no other durable knowledge.
419-
- Invoke any sibling skill except `sce-decision`, or invoke `sce-decision`
420-
outside the decision gate in successful context synchronization.
419+
- Invoke any sibling SCE skill, sibling SCE package, or SCE workflow command
420+
except `sce-decision`, or invoke `sce-decision` outside the decision gate in
421+
successful context synchronization.
421422
- Delete a context file that has uncommitted changes.
422423
- \(returnExecutionStyleResult.render.apply(mode)).
423424
"""
@@ -749,8 +750,9 @@ local planRoleData = new SyncRole {
749750
- Create the context root. `sce setup --bootstrap-context` owns that.
750751
- Narrate changed files as documentation. Feature existence is the only reason
751752
to document a change that introduced no other durable knowledge.
752-
- Invoke any sibling skill except `sce-decision`, or invoke `sce-decision`
753-
outside the decision gate in successful context synchronization.
753+
- Invoke any sibling SCE skill, sibling SCE package, or SCE workflow command
754+
except `sce-decision`, or invoke `sce-decision` outside the decision gate in
755+
successful context synchronization.
754756
- Delete a context file that has uncommitted changes.
755757
- \(returnInternalState.render.apply(mode)).
756758
"""
@@ -1390,8 +1392,9 @@ Do not:
13901392
- Create the context root. `sce setup --bootstrap-context` owns that.
13911393
- Narrate changed files as documentation. Feature existence is the only reason to
13921394
document a change that introduced no other durable knowledge.
1393-
- Invoke any sibling skill except `sce-decision`, or invoke `sce-decision`
1394-
outside the decision gate in successful context synchronization.
1395+
- Invoke any sibling SCE skill, sibling SCE package, or SCE workflow command
1396+
except `sce-decision`, or invoke `sce-decision` outside the decision gate in
1397+
successful context synchronization.
13951398
- Delete a context file that has uncommitted changes.
13961399
- Return an execution-style internal state.
13971400
"""

config/pkl/base/workflow-handover.pkl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,17 @@ local titleAndPurpose = model.packageOnlyBlock.apply("""
2929
user-visible response. This is a two-mode, single-pass workflow: writer mode
3030
captures the current session and repository state as one durable handover
3131
document; loader mode reads back an existing handover for continuation in
32-
another session. Neither mode invokes another skill, sibling package, or
33-
workflow command, and neither mode waits for user input mid-run.
32+
another session. Neither mode invokes another SCE skill, sibling SCE package, or
33+
SCE workflow command, and neither mode waits for user input mid-run.
3434
3535
## User-visible output
3636
3737
Use `references/output.md` for every terminal response. Render no raw internal
3838
state. The reference contains only human-visible Markdown layouts.
3939
User-visible output is limited to those layouts: never invent a layout, and
4040
never wrap one in an added preamble, commentary, summary, or extra section.
41+
42+
\(model.helperSkillCompositionRule)
4143
""")
4244

4345
local renderPersistedFormatBody = """
@@ -220,7 +222,7 @@ local renderSkillBody = (mode: model.WorkflowRenderMode) -> """
220222
one handover document it writes.
221223
- Loader mode never edits a file, writes a new file, or changes plan or task
222224
state.
223-
- Never invoke another skill, sibling package, or workflow command.
225+
- Never invoke another SCE skill, sibling SCE package, or SCE workflow command.
224226
- Never treat a file outside `context/handovers/`, or a non-Markdown file, as a
225227
loadable handover.
226228
- Never create the `context/` root; `sce setup --bootstrap-context` owns that.

config/pkl/check-generated.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,18 @@ expect_pkl_fixture_failure \
9696
expect_pkl_fixture_failure \
9797
"config/pkl/renderers/fixtures/output-dedup-check.pkl" \
9898
"generated SKILL.md reproduces a references/output.md fenced layout verbatim"
99+
expect_pkl_fixture_failure \
100+
"config/pkl/renderers/fixtures/unscoped-skill-prohibition-check.pkl" \
101+
"generated workflow skill contains an unscoped skill prohibition"
102+
expect_pkl_fixture_failure \
103+
"config/pkl/renderers/fixtures/missing-helper-skill-rule-check.pkl" \
104+
"generated workflow skill is missing the required helper-skill composition rule"
105+
expect_pkl_fixture_failure \
106+
"config/pkl/renderers/fixtures/opencode-arbitrary-sce-permission-check.pkl" \
107+
"OpenCode skill permissions must reject arbitrary SCE permissions"
108+
expect_pkl_fixture_failure \
109+
"config/pkl/renderers/fixtures/opencode-skill-permission-order-check.pkl" \
110+
"OpenCode skill permissions must preserve the wildcard, deny, and explicit-allow order"
99111

100112
"$producer" "$repo_root" "$generated_input_root"
101113
generated_root="$generated_input_root/pkl-generated"

config/pkl/renderers/fixtures/blank-line-run-check.pkl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ local documentsWithBlankLineRun = new Mapping {
1111
result = if (
1212
documentsWithBlankLineRun.every((_, text) -> !text.contains("\n\n\n"))
1313
) "negative fixture unexpectedly contained no blank-line run"
14-
else error("generated workflow document contains two or more consecutive blank lines")
14+
else throw("generated workflow document contains two or more consecutive blank lines")

config/pkl/renderers/fixtures/extra-artifact-check.pkl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ result = if (
1111
artifactsWithExtraPath.length == contract.expectedArtifactPaths.length
1212
&& contract.expectedArtifactPaths.every((path, _) -> artifactsWithExtraPath.containsKey(path))
1313
) "negative fixture unexpectedly matched"
14-
else error("generated artifact inventory does not match the exact expected path contract")
14+
else throw("generated artifact inventory does not match the exact expected path contract")

config/pkl/renderers/fixtures/forbidden-workflow-reference-check.pkl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ local documentsWithForbiddenReference = new Mapping {
1111
result = if (
1212
documentsWithForbiddenReference.every((_, text) -> !text.contains("sce-plan-review"))
1313
) "negative fixture unexpectedly contained no forbidden reference"
14-
else error("generated workflow document contains a forbidden sibling-package reference or unresolved internalization token")
14+
else throw("generated workflow document contains a forbidden sibling-package reference or unresolved internalization token")

0 commit comments

Comments
 (0)