@wichayutdew/pi-workflows 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +30 -0
  2. package/dist/index.js +141 -17
  3. package/examples/starter-kit/agents/planner.md +4 -0
  4. package/examples/starter-kit/agents/reviewer.md +4 -0
  5. package/examples/starter-kit/agents/scout.md +4 -0
  6. package/examples/starter-kit/agents/worker.md +4 -0
  7. package/examples/starter-kit/agents/workspace-preparer.md +4 -0
  8. package/examples/starter-kit/investigate.workflow.yaml +34 -64
  9. package/examples/starter-kit/jira.workflow.yaml +75 -0
  10. package/examples/starter-kit/mr-comment.workflow.yaml +48 -115
  11. package/examples/starter-kit/mr-review.workflow.yaml +36 -93
  12. package/examples/starter-kit/settings.yaml +2 -1
  13. package/examples/starter-kit/steps/investigate/investigate.md +18 -55
  14. package/examples/starter-kit/steps/investigate/retrieve.md +15 -50
  15. package/examples/starter-kit/steps/investigate/validate.md +10 -36
  16. package/examples/starter-kit/steps/jira/create.md +25 -0
  17. package/examples/starter-kit/steps/jira/draft.md +18 -0
  18. package/examples/starter-kit/steps/jira/plan.md +30 -0
  19. package/examples/starter-kit/steps/mr-comment/checkout-source.md +7 -60
  20. package/examples/starter-kit/steps/mr-comment/fetch.md +11 -36
  21. package/examples/starter-kit/steps/mr-comment/implement.md +10 -34
  22. package/examples/starter-kit/steps/mr-comment/plan.md +47 -70
  23. package/examples/starter-kit/steps/mr-comment/publish.md +11 -32
  24. package/examples/starter-kit/steps/mr-comment/verify.md +8 -42
  25. package/examples/starter-kit/steps/mr-review/fetch.md +8 -46
  26. package/examples/starter-kit/steps/mr-review/publish-approved.md +7 -38
  27. package/examples/starter-kit/steps/mr-review/review-for-approval.md +24 -113
  28. package/examples/starter-kit/steps/mr-review/verify-published.md +9 -30
  29. package/examples/starter-kit/steps/shared/prepare-workspace.md +9 -105
  30. package/examples/starter-kit/steps/shared/publish-remote.md +8 -37
  31. package/examples/starter-kit/steps/ticket/implement.md +11 -58
  32. package/examples/starter-kit/steps/ticket/plan.md +49 -171
  33. package/examples/starter-kit/steps/ticket/verify.md +12 -98
  34. package/examples/starter-kit/steps/work/implement.md +11 -58
  35. package/examples/starter-kit/steps/work/plan.md +42 -130
  36. package/examples/starter-kit/steps/work/verify.md +11 -58
  37. package/examples/starter-kit/ticket.workflow.yaml +38 -81
  38. package/examples/starter-kit/work.workflow.yaml +34 -72
  39. package/package.json +7 -4
  40. package/schemas/workflow.schema.json +61 -0
  41. package/scripts/patch-herdr-agent-state.mjs +36 -0
  42. package/src/config/types.ts +9 -0
  43. package/src/config/validation/step.ts +117 -0
  44. package/src/harness/artifact-contract.ts +46 -0
  45. package/src/harness/gate-submission-action.ts +28 -0
  46. package/src/harness/status-actions.ts +19 -0
  47. package/src/herdr-workflow-state.ts +65 -0
package/README.md CHANGED
@@ -36,6 +36,36 @@ dictating your language, framework, or delivery process.
36
36
  `reviewer`, or `scout`; customize profiles under your user workflow
37
37
  directory's `agents/` folder.
38
38
 
39
+ ## Herdr workflow status
40
+
41
+ When Pi runs delegated workflow steps, the parent agent may be idle while child work continues. Pi Workflows includes a Herdr companion extension that reports workflow lifecycle state so the pane remains **working** until the workflow completes, pauses, or is interrupted.
42
+
43
+ The companion is inactive unless Herdr provides `HERDR_ENV=1`, `HERDR_SOCKET_PATH`, and `HERDR_PANE_ID`. It uses Herdr's existing managed Pi reporter, so it does not create a competing pane agent.
44
+
45
+ ### Install from npm
46
+
47
+ ```bash
48
+ pi install npm:@wichayutdew/pi-workflows
49
+ ```
50
+
51
+ Start a new Pi session in Herdr, then run a workflow normally. Herdr should show the pane as working while the workflow is active and display the terminal workflow message after completion or interruption.
52
+
53
+ ### Reapply after a Herdr update
54
+
55
+ Herdr manages `~/.pi/agent/extensions/herdr-agent-state.ts`; an integration update can replace the workflow lifecycle patch. Locate the installed package with `pi list`, change into that package directory, and run:
56
+
57
+ ```bash
58
+ npm run patch:herdr
59
+ ```
60
+
61
+ The script patches the managed integration at its default location. It is idempotent, so it is safe to run after every Herdr update. To patch a non-default integration path, set `HERDR_PI_EXTENSION_PATH` before running the script:
62
+
63
+ ```bash
64
+ HERDR_PI_EXTENSION_PATH=/path/to/herdr-agent-state.ts npm run patch:herdr
65
+ ```
66
+
67
+ Restart Pi or run `/reload` after patching. If Herdr's managed extension changes shape and the script reports an unsupported layout, do not edit it manually; update Pi Workflows or report the integration change.
68
+
39
69
  ## Explore
40
70
 
41
71
  - [Getting started guide](./GETTING_STARTED.md) — install Pi Workflows and
package/dist/index.js CHANGED
@@ -518,6 +518,75 @@ function parseTransitions(value, path, errors) {
518
518
  }
519
519
  return transitions;
520
520
  }
521
+ function parseArtifactContract(value, path, errors) {
522
+ if (value === undefined)
523
+ return;
524
+ if (!isJsonObject(value)) {
525
+ errors.push(`${path}: expected an object`);
526
+ return;
527
+ }
528
+ rejectUnknownKeys(value, [
529
+ "maxChars",
530
+ "requiredSubstrings",
531
+ "forbiddenSubstrings",
532
+ "equalOccurrenceGroups",
533
+ "onValidationFailure"
534
+ ], path, errors);
535
+ if (value.maxChars === undefined) {
536
+ errors.push(`${path}.maxChars: expected an integer from 1 to 200000`);
537
+ }
538
+ const maxChars = readInteger(value.maxChars, 200000, `${path}.maxChars`, errors, { min: 1, max: 200000 });
539
+ const parseSubstrings = (field) => {
540
+ const values = readStringList(value[field], `${path}.${field}`, errors, /.+/);
541
+ if (values.length > 32) {
542
+ errors.push(`${path}.${field}: at most 32 values are allowed`);
543
+ }
544
+ values.forEach((substring, index) => {
545
+ if (substring.length > 1024) {
546
+ errors.push(`${path}.${field}[${index}]: exceeds 1024 characters`);
547
+ }
548
+ });
549
+ return values;
550
+ };
551
+ const equalOccurrenceGroups = (() => {
552
+ if (value.equalOccurrenceGroups === undefined)
553
+ return [];
554
+ if (!Array.isArray(value.equalOccurrenceGroups)) {
555
+ errors.push(`${path}.equalOccurrenceGroups: expected an array`);
556
+ return [];
557
+ }
558
+ if (value.equalOccurrenceGroups.length > 32) {
559
+ errors.push(`${path}.equalOccurrenceGroups: at most 32 groups are allowed`);
560
+ }
561
+ return value.equalOccurrenceGroups.reduce((groups, group, index) => {
562
+ const groupPath = `${path}.equalOccurrenceGroups[${index}]`;
563
+ const values = readStringList(group, groupPath, errors, /.+/);
564
+ if (values.length < 2) {
565
+ errors.push(`${groupPath}: at least two values are required`);
566
+ }
567
+ if (values.length > 32) {
568
+ errors.push(`${groupPath}: at most 32 values are allowed`);
569
+ }
570
+ values.forEach((substring, valueIndex) => {
571
+ if (substring.length > 1024) {
572
+ errors.push(`${groupPath}[${valueIndex}]: exceeds 1024 characters`);
573
+ }
574
+ });
575
+ return [...groups, values];
576
+ }, []);
577
+ })();
578
+ const onValidationFailure = value.onValidationFailure === undefined ? undefined : readString(value.onValidationFailure, `${path}.onValidationFailure`, errors);
579
+ if (onValidationFailure !== undefined && onValidationFailure !== "retry") {
580
+ errors.push(`${path}.onValidationFailure: expected retry`);
581
+ }
582
+ return {
583
+ maxChars,
584
+ requiredSubstrings: parseSubstrings("requiredSubstrings"),
585
+ forbiddenSubstrings: parseSubstrings("forbiddenSubstrings"),
586
+ equalOccurrenceGroups,
587
+ ...onValidationFailure === "retry" ? { onValidationFailure } : {}
588
+ };
589
+ }
521
590
  function parseGate(value, path, errors) {
522
591
  if (value === undefined)
523
592
  return;
@@ -530,7 +599,8 @@ function parseGate(value, path, errors) {
530
599
  "submitOutcome",
531
600
  "approvedOutcome",
532
601
  "rejectedOutcome",
533
- "timeoutMs"
602
+ "timeoutMs",
603
+ "artifactContract"
534
604
  ], path, errors);
535
605
  const providerValue = value.provider === undefined ? "prompt" : readString(value.provider, `${path}.provider`, errors);
536
606
  const provider = providerValue === "prompt" || providerValue === "plannotator" ? providerValue : undefined;
@@ -540,6 +610,7 @@ function parseGate(value, path, errors) {
540
610
  const submitOutcome = readString(value.submitOutcome, `${path}.submitOutcome`, errors, { pattern: OUTCOME_PATTERN });
541
611
  const approvedOutcome = readString(value.approvedOutcome, `${path}.approvedOutcome`, errors, { pattern: OUTCOME_PATTERN });
542
612
  const rejectedOutcome = readString(value.rejectedOutcome, `${path}.rejectedOutcome`, errors, { pattern: OUTCOME_PATTERN });
613
+ const artifactContract = parseArtifactContract(value.artifactContract, `${path}.artifactContract`, errors);
543
614
  if (provider === "prompt" && value.timeoutMs !== undefined) {
544
615
  errors.push(`${path}.timeoutMs: only valid with provider "plannotator"`);
545
616
  }
@@ -553,12 +624,14 @@ function parseGate(value, path, errors) {
553
624
  provider,
554
625
  submitOutcome,
555
626
  approvedOutcome,
556
- rejectedOutcome
627
+ rejectedOutcome,
628
+ ...artifactContract ? { artifactContract } : {}
557
629
  } : {
558
630
  provider,
559
631
  submitOutcome,
560
632
  approvedOutcome,
561
633
  rejectedOutcome,
634
+ ...artifactContract ? { artifactContract } : {},
562
635
  timeoutMs: readInteger(value.timeoutMs, 30000, `${path}.timeoutMs`, errors, { min: 1000, max: 30000 })
563
636
  };
564
637
  }
@@ -649,6 +722,9 @@ function parseWorkflowStep(value, stepId, path, errors) {
649
722
  if (Object.hasOwn(transitions, gate.submitOutcome)) {
650
723
  errors.push(`${path}.transitions: submitOutcome is handled by the gate and must not be a transition`);
651
724
  }
725
+ if (gate.artifactContract?.onValidationFailure === "retry" && !Object.hasOwn(transitions, "retry")) {
726
+ errors.push(`${path}.transitions: artifact-contract retry requires a "retry" transition`);
727
+ }
652
728
  }
653
729
  if (workspace) {
654
730
  workspace.bindOn.forEach((outcome) => {
@@ -1595,19 +1671,6 @@ function createSubagentDelegationClient(spawnWorker = spawn) {
1595
1671
  };
1596
1672
  }
1597
1673
 
1598
- class SubagentDelegationClient {
1599
- #client = createSubagentDelegationClient();
1600
- get activeRequestId() {
1601
- return this.#client.activeRequestId;
1602
- }
1603
- delegate(request, options) {
1604
- return this.#client.delegate(request, options);
1605
- }
1606
- cancelActiveAndWait(waitMs) {
1607
- return this.#client.cancelActiveAndWait(waitMs);
1608
- }
1609
- }
1610
-
1611
1674
  // src/policy/completion-batch.ts
1612
1675
  var isRecord3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
1613
1676
  var toolCalls = (message) => {
@@ -4005,6 +4068,13 @@ function workflowStatusSnapshot() {
4005
4068
  }
4006
4069
  function updateStatus() {
4007
4070
  refreshStatusWhileRunning.call(this);
4071
+ const run = this.run;
4072
+ this.pi.events.emit("pi-workflows:state", {
4073
+ state: run?.status === "running" ? "working" : run?.status === "awaiting-gate" || run?.status === "paused" ? "blocked" : run?.status === "completed" ? "completed" : "interrupted",
4074
+ workflowId: run?.workflowId,
4075
+ stepId: run?.currentStepId,
4076
+ message: run?.status === "completed" ? `Workflow "${run.workflowId}" completed` : run?.status === "paused" ? run.pauseReason : undefined
4077
+ });
4008
4078
  if (!this.latestContext)
4009
4079
  return;
4010
4080
  if (this.legacyProgressWidgetContext !== this.latestContext) {
@@ -6700,6 +6770,44 @@ function createDelegationControlActions() {
6700
6770
  };
6701
6771
  }
6702
6772
 
6773
+ // src/harness/artifact-contract.ts
6774
+ function countOccurrences(value, substring) {
6775
+ let count = 0;
6776
+ let offset = 0;
6777
+ while (true) {
6778
+ const match = value.indexOf(substring, offset);
6779
+ if (match === -1)
6780
+ return count;
6781
+ count += 1;
6782
+ offset = match + substring.length;
6783
+ }
6784
+ }
6785
+ function validateArtifactContract(artifact, contract) {
6786
+ if (!contract)
6787
+ return;
6788
+ if (artifact.length > contract.maxChars) {
6789
+ return `gate artifact exceeds ${contract.maxChars} characters`;
6790
+ }
6791
+ const required = contract.requiredSubstrings.find((substring) => !artifact.includes(substring));
6792
+ if (required) {
6793
+ return `gate artifact is missing required text: ${JSON.stringify(required)}`;
6794
+ }
6795
+ const forbidden = contract.forbiddenSubstrings.find((substring) => artifact.includes(substring));
6796
+ if (forbidden) {
6797
+ return `gate artifact contains forbidden text: ${JSON.stringify(forbidden)}`;
6798
+ }
6799
+ for (const group of contract.equalOccurrenceGroups) {
6800
+ const counts = group.map((substring) => countOccurrences(artifact, substring));
6801
+ if (counts.some((count) => count === 0)) {
6802
+ return `gate artifact is missing required repeated text: ${JSON.stringify(group)}`;
6803
+ }
6804
+ if (!counts.every((count) => count === counts[0])) {
6805
+ return `gate artifact has unequal repeated text counts: ${JSON.stringify(group)}`;
6806
+ }
6807
+ }
6808
+ return;
6809
+ }
6810
+
6703
6811
  // src/harness/gate-submission-action.ts
6704
6812
  function isCurrentGateRequest(run, originalRun, requestId) {
6705
6813
  return run !== undefined && run.runId === originalRun.runId && run.currentStepId === originalRun.currentStepId && run.pendingGate?.requestId === requestId && run.pendingGate.reviewId === undefined && (run.status === "awaiting-gate" || run.status === "paused");
@@ -6710,6 +6818,22 @@ async function submitGate(workflow, originalRun, outcome, summary, artifact) {
6710
6818
  const step = workflow.definition.steps[originalRun.currentStepId];
6711
6819
  if (!step?.gate)
6712
6820
  throw new Error("Current step has no gate");
6821
+ const contractError = validateArtifactContract(artifact, step.gate.artifactContract);
6822
+ if (contractError) {
6823
+ if (step.gate.artifactContract?.onValidationFailure !== "retry") {
6824
+ throw new Error(contractError);
6825
+ }
6826
+ const retrySummary = `Artifact contract failed: ${contractError}`;
6827
+ this.run = advanceRun(workflow, originalRun, "retry", retrySummary, this.dependencies.now());
6828
+ this.persist();
6829
+ this.updateStatus();
6830
+ this.settleAfterTransition(workflow, {
6831
+ stepId: originalRun.currentStepId,
6832
+ outcome: "retry",
6833
+ summary: retrySummary
6834
+ });
6835
+ return;
6836
+ }
6713
6837
  this.run = beginGate(workflow, originalRun, outcome, artifact, requestId, this.dependencies.now(), summary);
6714
6838
  this.persist();
6715
6839
  this.restoreBaselineTools();
@@ -7696,6 +7820,6 @@ function createPiWorkflowsExtension(dependencies) {
7696
7820
  var piWorkflowsExtension = createPiWorkflowsExtension(DEFAULT_DEPENDENCIES4);
7697
7821
  var src_default = piWorkflowsExtension;
7698
7822
  export {
7699
- src_default as default,
7700
- createPiWorkflowsExtension
7823
+ createPiWorkflowsExtension,
7824
+ src_default as default
7701
7825
  };
@@ -1,3 +1,7 @@
1
+ ---
2
+ thinking: high
3
+ ---
4
+
1
5
  You are the planning role for one workflow step.
2
6
 
3
7
  Stay within the step's declared permissions. Establish facts before conclusions,
@@ -1,3 +1,7 @@
1
+ ---
2
+ thinking: xhigh
3
+ ---
4
+
1
5
  You are the independent review role for one workflow step.
2
6
 
3
7
  Remain read-only unless the step explicitly grants a different authority.
@@ -1,3 +1,7 @@
1
+ ---
2
+ thinking: medium
3
+ ---
4
+
1
5
  You are the investigation role for one workflow step.
2
6
 
3
7
  Stay read-only. Gather the smallest set of decisive evidence, distinguish facts
@@ -1,3 +1,7 @@
1
+ ---
2
+ thinking: high
3
+ ---
4
+
1
5
  You are the implementation role for one workflow step.
2
6
 
3
7
  Treat the approved workflow artifact and declared permissions as the complete
@@ -1,3 +1,7 @@
1
+ ---
2
+ thinking: low
3
+ ---
4
+
1
5
  You are the workspace preparation role for one workflow step.
2
6
 
3
7
  Inspect the current repository state before mutation. Create or reuse only the
@@ -1,7 +1,7 @@
1
1
  version: 1
2
2
  id: investigate
3
3
  command: investigate
4
- description: Derive, approve, investigate, and independently validate an evidence-backed finding
4
+ description: 'Derive, approve, investigate, and independently validate an evidence-backed finding. Example: /investigate PROJ-123 Root cause of elevated 500 error rates in checkout service'
5
5
  start: retrieve
6
6
  maxStepVisits: 12
7
7
  summaryMaxChars: 30000
@@ -12,48 +12,30 @@ steps:
12
12
  file: steps/investigate/retrieve.md
13
13
  agent: scout
14
14
  permissions:
15
- tools: [read, ls, bash]
16
- mcp: &jira-read-mcp
17
- - atlassian/atlassian_getAccessibleAtlassianResources
18
- - atlassian/atlassian_getJiraIssue
19
- - atlassian/atlassian_getJiraIssueRemoteIssueLinks
20
- - atlassian/atlassian_searchJiraIssuesUsingJql
21
- extensions: [pi-web-tools]
22
- skills: &retrieve-skills [caveman, coding-standards, brainstorming]
15
+ tools: [read, ls, bash, mcp]
16
+ mcp: [atlassian, context7, sourcegraph, glean, grafana]
17
+ extensions: [/]
18
+ skills: [jira-ticket, start-triage, search-code-sourcegraph, caveman]
23
19
  bash:
24
- mode: allow-list
25
- allow: &inspection-bash
26
- - executable: grep
27
- - executable: head
28
- - executable: ls
29
- - executable: pwd
30
- - executable: rg
31
- - executable: stat
32
- - executable: tail
33
- - executable: wc
34
- - executable: git
35
- argsPrefixes:
36
- [
37
- [status],
38
- [diff],
39
- [grep],
40
- [log],
41
- [ls-files],
42
- [rev-parse],
43
- [show],
44
- [merge-base],
45
- [rev-list],
46
- [worktree, list],
47
- ]
48
- requires:
49
- tools: [read, ls, bash]
50
- skills: *retrieve-skills
20
+ mode: unrestricted
51
21
  gate:
52
22
  provider: plannotator
53
23
  submitOutcome: submit
54
24
  approvedOutcome: approved
55
25
  rejectedOutcome: changes-requested
56
26
  timeoutMs: 30000
27
+ artifactContract:
28
+ maxChars: 6000
29
+ requiredSubstrings:
30
+ - '## Brief description'
31
+ - '## Goals'
32
+ - '## Boundaries'
33
+ - '## Evidence & sources'
34
+ - '## Report destination'
35
+ - '## Open evidence gaps'
36
+ forbiddenSubstrings:
37
+ - 'The complete gate artifact is the exact Markdown saved at'
38
+ onValidationFailure: retry
57
39
  transitions:
58
40
  approved: investigate
59
41
  changes-requested: retrieve
@@ -65,23 +47,20 @@ steps:
65
47
  file: steps/investigate/investigate.md
66
48
  agent: worker
67
49
  permissions:
68
- tools: [read, ls, bash, edit, write]
69
- mcp: &investigation-mcp
70
- - atlassian
71
- - gitlab
72
- - glean
73
- - superset
74
- - sourcegraph
75
- - context7/resolve-library-id
76
- - context7/query-docs
77
- - gh_grep/searchGitHub
78
- extensions: [pi-web-tools]
79
- skills: &investigation-skills [caveman, coding-standards]
50
+ tools: [read, ls, bash, write, mcp]
51
+ mcp: [atlassian, context7, sourcegraph, glean, grafana, gitlab]
52
+ extensions: [/]
53
+ skills:
54
+ [
55
+ coding-standards,
56
+ systematic-debugging,
57
+ start-triage,
58
+ grafana-logs,
59
+ search-code-sourcegraph,
60
+ caveman,
61
+ ]
80
62
  bash:
81
63
  mode: unrestricted
82
- requires:
83
- tools: [read, ls, bash, edit, write]
84
- skills: *investigation-skills
85
64
  transitions:
86
65
  ready: validate
87
66
  retry: investigate
@@ -92,21 +71,12 @@ steps:
92
71
  file: steps/investigate/validate.md
93
72
  agent: reviewer
94
73
  permissions:
95
- tools: [read, ls, bash]
96
- mcp: *investigation-mcp
97
- extensions: [pi-web-tools]
98
- skills:
99
- &validation-skills [
100
- caveman,
101
- coding-standards,
102
- systematic-debugging,
103
- verification-before-completion,
104
- ]
74
+ tools: [read, ls, bash, mcp]
75
+ mcp: [atlassian, sourcegraph, glean, gitlab]
76
+ extensions: [/]
77
+ skills: [verification-before-completion, caveman]
105
78
  bash:
106
79
  mode: unrestricted
107
- requires:
108
- tools: [read, ls, bash]
109
- skills: *validation-skills
110
80
  transitions:
111
81
  approved: $done
112
82
  gaps: investigate
@@ -0,0 +1,75 @@
1
+ version: 1
2
+ id: jira
3
+ command: jira
4
+ description: 'Draft, approve, and create a Jira Epic with ordered Stories. Example: /jira ACTB Implement multi-region failover for pricing service'
5
+ start: draft
6
+ maxStepVisits: 12
7
+ summaryMaxChars: 30000
8
+ steps:
9
+ draft:
10
+ title: Normalize Epic and Story input
11
+ prompt:
12
+ file: steps/jira/draft.md
13
+ agent: scout
14
+ permissions:
15
+ tools: [read, ls, bash]
16
+ mcp: []
17
+ extensions: [/]
18
+ skills: [jira-ticket, caveman]
19
+ bash:
20
+ mode: unrestricted
21
+ transitions:
22
+ ready: plan
23
+ retry: draft
24
+ blocked: $pause
25
+ plan:
26
+ title: Validate Jira schema and approve Epic and Story plan
27
+ prompt:
28
+ file: steps/jira/plan.md
29
+ agent: planner
30
+ permissions:
31
+ tools: [read, ls, bash, mcp]
32
+ mcp: [atlassian]
33
+ extensions: [/]
34
+ skills: [jira-ticket, caveman]
35
+ bash:
36
+ mode: unrestricted
37
+ gate:
38
+ provider: plannotator
39
+ submitOutcome: submit
40
+ approvedOutcome: approved
41
+ rejectedOutcome: changes-requested
42
+ timeoutMs: 30000
43
+ artifactContract:
44
+ maxChars: 16000
45
+ requiredSubstrings:
46
+ - '# Create Jira Epic and Stories'
47
+ - '## Jira field contract'
48
+ - '## Epic'
49
+ - '## Ordered Stories'
50
+ - '## Creation sequence'
51
+ - '## Safety limits'
52
+ forbiddenSubstrings:
53
+ - 'The complete gate artifact is the exact Markdown saved at'
54
+ onValidationFailure: retry
55
+ transitions:
56
+ approved: create
57
+ changes-requested: plan
58
+ retry: plan
59
+ blocked: $pause
60
+ create:
61
+ title: Create approved Jira Epic and Stories
62
+ prompt:
63
+ file: steps/jira/create.md
64
+ agent: worker
65
+ permissions:
66
+ tools: [read, ls, bash, mcp]
67
+ mcp: [atlassian]
68
+ extensions: [/]
69
+ skills: [jira-ticket, caveman]
70
+ bash:
71
+ mode: unrestricted
72
+ transitions:
73
+ ready: $done
74
+ retry: create
75
+ blocked: $pause