@farmslot/skills 0.2.0 → 0.4.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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,15 @@ All notable changes to `@farmslot/skills` are tracked here.
6
6
 
7
7
  - Active-development baseline; add user-facing changes here before release or package publication.
8
8
 
9
+ ## 0.4.0 - 2026-09-17
10
+
11
+ - Recipe Cook lane: the runner envelope reports `terminal_status` (`done|blocked|failed`) and `terminal_reason`; the lane no longer reads or rewrites a `STATUS:` line in the run-local TASK.md, and `meta.json` records `terminal_reason`. Templates and skill text drop the field. An envelope without a valid `terminal_status`, or a blocked/failed one without `terminal_reason`, fails the run; the model output stays in `runner-output.txt`. The `tmux-model-driver` launcher and watcher stop mentioning or parsing the field.
12
+
13
+ ## 0.3.0 - 2026-08-03
14
+
15
+ - Teach recipe authors to start from existing parameterized recipes and compose them node by node before adding new capabilities.
16
+ - Active-development baseline; add user-facing changes here before release or package publication.
17
+
9
18
  ## 0.2.0 - 2026-07-19
10
19
 
11
20
  - Teach recipe authors to inspect declared actions and reusable flows before creating new capabilities
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farmslot/skills",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -33,7 +33,7 @@
33
33
  "prepublishOnly": "node ../../scripts/quality/check-farmslot-package-readiness.mjs --publish --packages @farmslot/skills"
34
34
  },
35
35
  "dependencies": {
36
- "@farmslot/agent-runtime": "0.2.0"
36
+ "@farmslot/agent-runtime": "0.10.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^22.0.0",
@@ -222,9 +222,7 @@ function resolveRepoLocalContext({ startDir, repoName }) {
222
222
  const repoRoot = findUp(startDir, (candidate) => {
223
223
  if (!fs.existsSync(path.join(candidate, '.git'))) return false;
224
224
  if (repoName === 'example-browser') {
225
- return (
226
- findFilesByBasename(candidate, ['validate-recipe.js', 'validate-flow-schema.js']).length > 0
227
- );
225
+ return findFilesByBasename(candidate, ['validate-recipe.js']).length > 0;
228
226
  }
229
227
  if (repoName === 'example-mobile') {
230
228
  return findFilesByBasename(candidate, ['validate-recipe.sh']).length > 0;
@@ -431,7 +429,9 @@ function buildPrompt({
431
429
  '',
432
430
  '## Required output JSON schema',
433
431
  '{',
434
- ' "task_markdown": "full TASK.md content based on the provided template. Set STATUS to the correct terminal value: done, blocked, or failed. Inside ## Validation Evidence include the exact standalone token RECIPE_COOK_VALIDATION_PENDING.",',
432
+ ' "task_markdown": "full TASK.md content based on the provided template. Inside ## Validation Evidence include the exact standalone token RECIPE_COOK_VALIDATION_PENDING.",',
433
+ ' "terminal_status": "done|blocked|failed",',
434
+ ' "terminal_reason": "one sentence naming the concrete blocker or failure; null when done",',
435
435
  ' "recipe_json": { "valid": "json object" },',
436
436
  ' "recipe_cook_json": { "valid": "json object" } or null,',
437
437
  ' "evidence_verdict": "good|ok|bad",',
@@ -453,7 +453,8 @@ function buildPrompt({
453
453
  '',
454
454
  'The JSON object you return is the final serialized payload for those files.',
455
455
  'Emit the JSON last, after you have fully reasoned through:',
456
- '- the rewritten TASK.md contents, including the correct terminal STATUS',
456
+ '- the rewritten TASK.md contents',
457
+ '- the terminal_status (done, blocked, or failed) and, for blocked or failed, its concrete terminal_reason',
457
458
  '- the recipe artifact contents',
458
459
  '- the learning artifact contents',
459
460
  '- what validation commands should run',
@@ -661,17 +662,11 @@ function discoverValidationSteps({ repoName, repoRoot, recipePath, artifactsDir,
661
662
  }
662
663
 
663
664
  if (repoName === 'example-browser') {
664
- const schemaValidator = findFilesByBasename(repoRoot, ['validate-flow-schema.js'])[0];
665
665
  const recipeValidator = findFilesByBasename(repoRoot, ['validate-recipe.js'])[0];
666
- if (!schemaValidator || !recipeValidator) {
666
+ if (!recipeValidator) {
667
667
  return [];
668
668
  }
669
669
  const steps = [
670
- {
671
- name: 'schema_validation',
672
- command: ['node', schemaValidator, recipePath],
673
- cwd: repoRoot,
674
- },
675
670
  {
676
671
  name: 'dry_run',
677
672
  command: ['node', recipeValidator, '--recipe', recipePath, '--dry-run'],
@@ -777,26 +772,47 @@ function renderValidationEvidence(validationResults, cdpPort, repoRoot) {
777
772
  return lines.join('\n');
778
773
  }
779
774
 
780
- function deriveTerminalState(taskMarkdown, validationResults) {
775
+ // Terminal state comes from the runner's explicit envelope field, never from
776
+ // status text in the task markdown. Two evidence-based overrides remain: an
777
+ // UNRESOLVED proof target blocks, and a failed validation step fails.
778
+ function deriveTerminalState(envelope, taskMarkdown, validationResults) {
781
779
  const text = String(taskMarkdown || '');
782
- if (/\bSTATUS:\s*blocked\b/i.test(text)) {
783
- return { status: 'blocked', outcome: 'partial' };
780
+ // A reason only travels with a non-done envelope; a note attached to a done
781
+ // envelope must not be presented as the blocker or failure cause. The two
782
+ // evidence overrides below are reachable only for done envelopes, so they
783
+ // synthesize their own reason.
784
+ const reason =
785
+ envelope.terminal_status !== 'done' &&
786
+ typeof envelope.terminal_reason === 'string' &&
787
+ envelope.terminal_reason.trim()
788
+ ? envelope.terminal_reason.trim()
789
+ : null;
790
+ if (envelope.terminal_status === 'blocked') {
791
+ return { status: 'blocked', outcome: 'partial', reason };
784
792
  }
785
- if (/\bSTATUS:\s*failed\b/i.test(text)) {
786
- return { status: 'failed', outcome: 'failure' };
793
+ if (envelope.terminal_status === 'failed') {
794
+ return { status: 'failed', outcome: 'failure', reason };
787
795
  }
788
796
  if (/^\s*[-*]\s+.*:\s*UNRESOLVED\b/m.test(text)) {
789
- return { status: 'blocked', outcome: 'partial' };
797
+ return {
798
+ status: 'blocked',
799
+ outcome: 'partial',
800
+ reason: 'a required proof target remains UNRESOLVED',
801
+ };
790
802
  }
791
803
 
792
- const failingStep = Object.values(validationResults || {}).find(
793
- (result) => result.exit_code !== 0 && result.exit_code !== -1,
804
+ const failingStep = Object.entries(validationResults || {}).find(
805
+ ([, result]) => result.exit_code !== 0 && result.exit_code !== -1,
794
806
  );
795
807
  if (failingStep) {
796
- return { status: 'failed', outcome: 'failure' };
808
+ return {
809
+ status: 'failed',
810
+ outcome: 'failure',
811
+ reason: `validation step ${failingStep[0]} exited ${failingStep[1].exit_code}`,
812
+ };
797
813
  }
798
814
 
799
- return { status: 'done', outcome: 'success' };
815
+ return { status: 'done', outcome: 'success', reason: null };
800
816
  }
801
817
 
802
818
  function injectValidationEvidence(taskMarkdown, evidenceBlock) {
@@ -995,6 +1011,15 @@ async function main() {
995
1011
  typeof envelope.next_delta === 'string' && envelope.next_delta.trim(),
996
1012
  'Runner output missing next_delta',
997
1013
  );
1014
+ assert(
1015
+ ['done', 'blocked', 'failed'].includes(envelope.terminal_status),
1016
+ `Runner output missing terminal_status (done|blocked|failed). See ${path.join(runDir, 'runner-output.txt')}`,
1017
+ );
1018
+ assert(
1019
+ envelope.terminal_status === 'done' ||
1020
+ (typeof envelope.terminal_reason === 'string' && envelope.terminal_reason.trim()),
1021
+ `Runner output missing terminal_reason for terminal_status ${envelope.terminal_status}. See ${path.join(runDir, 'runner-output.txt')}`,
1022
+ );
998
1023
  writeJson(path.join(runDir, 'runner-response.json'), envelope);
999
1024
 
1000
1025
  const recipePath = path.join(artifactsDir, 'recipe.json');
@@ -1017,11 +1042,7 @@ async function main() {
1017
1042
  const evidenceBlock = renderValidationEvidence(validationResults, effectiveCdpPort, projectRoot);
1018
1043
  const normalizedTask = normalizeTaskArtifactDir(envelope.task_markdown, artifactsDir);
1019
1044
  let finalTask = injectValidationEvidence(normalizedTask, evidenceBlock);
1020
- const terminalState = deriveTerminalState(finalTask, validationResults);
1021
- finalTask = finalTask.replace(
1022
- /STATUS:\s*(pending|working|done|blocked|failed)/i,
1023
- `STATUS: ${terminalState.status}`,
1024
- );
1045
+ const terminalState = deriveTerminalState(envelope, finalTask, validationResults);
1025
1046
  writeFile(path.join(runDir, 'TASK.md'), finalTask);
1026
1047
 
1027
1048
  const verdict = mapVerdict(envelope.evidence_verdict, validationResults);
@@ -1044,6 +1065,7 @@ async function main() {
1044
1065
  : terminalState.outcome === 'partial'
1045
1066
  ? 'blocked'
1046
1067
  : 'completed',
1068
+ terminal_reason: terminalState.reason,
1047
1069
  });
1048
1070
  writeJson(path.join(artifactsDir, 'grade.json'), {
1049
1071
  recipe_semantic: verdict,
@@ -93,7 +93,7 @@ Completion requires:
93
93
  - unresolved-target audit written
94
94
  - validation evidence recorded
95
95
  - all required proof targets either `PROVEN` or explicitly non-applicable by contract
96
- - `STATUS: done` in the run-local copied `TASK.md`
96
+ - `terminal_status: done` in the run envelope; `blocked` and `failed` carry a concrete `terminal_reason`
97
97
 
98
98
  ## Hard Rules
99
99
 
@@ -101,7 +101,7 @@ Completion requires:
101
101
  - Do not emit canonical success output if every target is unresolved
102
102
  - Do not prefer screenshots when state proof is enough
103
103
  - Do not skip screenshots when the claim is visual or review-sensitive
104
- - Do not rebuild known flows as raw steps when `call` exists
104
+ - Do not rebuild known recipes as raw steps when `call` exists
105
105
  - Do not stop after printing a recipe or JSON payload if writable artifacts and validation surfaces still exist
106
106
  - Do not leave the copied `TASK.md` as the untouched template
107
107
  - Do not write `SIGNAL.json` with `complete/success` when a required validation failed or when a required proof target remains `UNRESOLVED`
@@ -112,14 +112,14 @@ Completion requires:
112
112
 
113
113
  | Pitfall | Correct Pattern |
114
114
  | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- |
115
- | Treating cooking as deterministic templating | Treat it as LLM synthesis constrained by repo flows/evals and honest proof |
115
+ | Treating cooking as deterministic templating | Treat it as LLM synthesis constrained by repo journeys/recipes/evals and honest proof |
116
116
  | Using the whole PR description as one target | Extract small executable proof targets |
117
117
  | Defaulting every UI claim to screenshots only | choose `visual` or `mixed` deliberately |
118
118
  | Defaulting every non-visual claim to a screenshot anyway | prefer state/log/trace proof |
119
- | Rewriting long repeated setup chains | reuse `call` flows |
119
+ | Rewriting long repeated setup chains | reuse parameterized recipes through `call` |
120
120
  | Forcing a canonical recipe when the claim is unresolved | stop or emit only resolved targets with explicit gaps |
121
121
  | Treating interactive farmslot execution like chat-only drafting | keep driving the workspace until artifacts + validation evidence exist |
122
- | Writing recipe artifacts but forgetting the copied `TASK.md` | rewrite the task file and update `STATUS` / evidence sections before stopping |
122
+ | Writing recipe artifacts but forgetting the copied `TASK.md` | rewrite the task file and update the evidence sections before stopping |
123
123
  | Inventing non-canonical runner fields like `timeout` on `wait_for` | use documented runner field names such as `timeout_ms` so live validation honors the intended timing |
124
124
 
125
125
  ## Stop Conditions
@@ -127,7 +127,7 @@ Completion requires:
127
127
  Stop and report instead of forcing a recipe when:
128
128
 
129
129
  - target text is still too ambiguous to map honestly
130
- - repo has no viable flow/eval/native interaction path for the claim
130
+ - repo has no viable recipe/eval/native interaction path for the claim
131
131
  - current runner context is missing and the missing context changes what should be proven
132
132
  - the repo has no validation runner and you already recorded `validation unavailable`
133
133
 
@@ -12,7 +12,6 @@ SOURCE_KIND:
12
12
  SOURCE_REF:
13
13
  ARTIFACT_DIR:
14
14
  VALIDATION_MODE:
15
- STATUS: pending
16
15
  ```
17
16
 
18
17
  ## Source Material
@@ -22,7 +21,7 @@ Paste or summarize:
22
21
  - PR body
23
22
  - linked ticket ACs / expected behavior
24
23
  - investigation findings
25
- - existing recipe / flow / eval refs
24
+ - existing action / recipe / eval refs
26
25
 
27
26
  ## Acceptance Criteria
28
27
 
@@ -87,19 +86,19 @@ It must include:
87
86
 
88
87
  ## Checklist
89
88
 
90
- - [ ] **1. Fill the task block** — set `TARGET_REPO`, `SOURCE_KIND`, `SOURCE_REF`, `ARTIFACT_DIR`, `VALIDATION_MODE`, `STATUS: working`.
91
- - [ ] **2. Gather the real source of truth** — PR/ticket/investigation text plus existing flows/evals in the target repo.
89
+ - [ ] **1. Fill the task block** — set `TARGET_REPO`, `SOURCE_KIND`, `SOURCE_REF`, `ARTIFACT_DIR`, `VALIDATION_MODE`.
90
+ - [ ] **2. Gather the real source of truth** — PR/ticket/investigation text plus existing user journeys, recipes, and evals in the target repo.
92
91
  - [ ] **3. Enumerate acceptance criteria** — write a numbered canonical list under `## Acceptance Criteria`.
93
92
  - [ ] **4. Extract proof targets** — split the work into the smallest executable claims.
94
93
  - [ ] **5. Decide proof mode** — choose `state`, `visual`, or `mixed` for every target and justify every `mixed`.
95
- - [ ] **6. Discover reuse** — list existing `flow` / `eval_ref` / canonical recipe surfaces before writing new graph steps.
94
+ - [ ] **6. Discover reuse** — list existing actions, recipes, and eval refs before writing new graph steps.
96
95
  - [ ] **7. Draft the recipe** — write `artifacts/recipe.json` and, when supported, `artifacts/recipe-cook.json`.
97
96
  - [ ] **8. Run schema validation** — if the repo provides a schema validator, run it and record the command + output under `## Validation Evidence`.
98
97
  - [ ] **9. Run runner dry-run validation** — if the repo provides a runner, run at least a dry-run and record the command + output.
99
98
  - [ ] **10. Handle unavailable validation honestly** — if the repo has no validator or runner, write `validation unavailable` with the reason. Do not fake success.
100
99
  - [ ] **11. Write the learning artifact** — write `artifacts/recipe-cook-learning.json` with evidence verdict and proposed next delta.
101
100
  - [ ] **12. Audit unresolved targets** — update `## Resolved vs Unresolved` and `## Recipe Coverage Audit` after validation evidence exists.
102
- - [ ] **13. Final completion gate** — set `STATUS: done` only after recipe writing, validation evidence, learning artifact, and unresolved-target audit are all present.
101
+ - [ ] **13. Final completion gate** — reach the Success outcome only after recipe writing, validation evidence, learning artifact, and unresolved-target audit are all present.
103
102
 
104
103
  ## Failure Conditions
105
104
 
@@ -11,8 +11,7 @@ Do not edit the template in-place during a cooking run.
11
11
  Every time you complete a checklist step:
12
12
 
13
13
  1. run `./mark N` if present (1-based); otherwise immediately change `- [ ]` to `- [x]`
14
- 2. update `STATUS` if needed
15
- 3. only then move to the next step
14
+ 2. only then move to the next step
16
15
 
17
16
  Do not batch checkbox updates.
18
17
  Do not leave completed steps unchecked.
@@ -31,7 +30,6 @@ TASK_DIR: {{TASK_DIR}}
31
30
  ARTIFACT_DIR: {{ARTIFACT_DIR}}
32
31
  SOURCE_BUNDLE_FILE: {{SOURCE_BUNDLE_FILE}}
33
32
  VALIDATION_MODE: {{VALIDATION_MODE}}
34
- STATUS: pending
35
33
  ```
36
34
 
37
35
  ## Source Material
@@ -43,7 +41,7 @@ Then replace this section with your own summary of:
43
41
  - PR body
44
42
  - linked ticket ACs / expected behavior
45
43
  - investigation findings
46
- - existing recipe / flow / eval refs
44
+ - existing action / recipe / eval refs
47
45
 
48
46
  ## Acceptance Criteria
49
47
 
@@ -110,12 +108,12 @@ It must include:
110
108
 
111
109
  ## Checklist
112
110
 
113
- - [ ] **1. Fill the task block** — set `TARGET_REPO`, `SOURCE_KIND`, `SOURCE_REF`, `ARTIFACT_DIR`, `VALIDATION_MODE`, `STATUS: working`.
114
- - [ ] **2. Gather the real source of truth** — PR/ticket/investigation text plus existing flows/evals in the target repo.
111
+ - [ ] **1. Fill the task block** — set `TARGET_REPO`, `SOURCE_KIND`, `SOURCE_REF`, `ARTIFACT_DIR`, `VALIDATION_MODE`.
112
+ - [ ] **2. Gather the real source of truth** — PR/ticket/investigation text plus existing user journeys, recipes, and evals in the target repo.
115
113
  - [ ] **3. Enumerate acceptance criteria** — write a numbered canonical list under `## Acceptance Criteria`.
116
114
  - [ ] **4. Extract proof targets** — split the work into the smallest executable claims.
117
115
  - [ ] **5. Decide proof mode** — choose `state`, `visual`, or `mixed` for every target and justify every `mixed`.
118
- - [ ] **6. Discover reuse** — list existing `flow` / `eval_ref` / canonical recipe surfaces before writing new graph steps.
116
+ - [ ] **6. Discover reuse** — list existing actions, recipes, and eval refs before writing new graph steps.
119
117
  - [ ] **7. Draft the recipe** — write `artifacts/recipe.json` and, when supported, `artifacts/recipe-cook.json`. Use documented runner field names for nodes (for example `timeout_ms` / `poll_ms` on `wait_for`), not guessed aliases.
120
118
  - [ ] **8. Rewrite this copied TASK file** — replace the template placeholders with the real run data and working notes. Leaving the untouched template is a failure.
121
119
  - [ ] **9. Run schema validation** — if the repo provides a schema validator, run it and record the command + output under `## Validation Evidence`.
@@ -124,7 +122,7 @@ It must include:
124
122
  - [ ] **12. Handle unavailable validation honestly** — if the repo has no validator or runner, write `validation unavailable` with the reason. Do not fake success.
125
123
  - [ ] **13. Write the learning artifact** — write `artifacts/recipe-cook-learning.json` with evidence verdict and proposed next delta.
126
124
  - [ ] **14. Audit unresolved targets** — update `## Resolved vs Unresolved` and `## Recipe Coverage Audit` after validation evidence exists.
127
- - [ ] **15. Final completion gate** — set `STATUS: done` only after recipe writing, rewritten TASK.md, validation evidence, learning artifact, and unresolved-target audit are all present.
125
+ - [ ] **15. Final completion gate** — reach the Success outcome only after recipe writing, rewritten TASK.md, validation evidence, learning artifact, and unresolved-target audit are all present.
128
126
  - [ ] **16. Write terminal signal** — write exactly one terminal `SIGNAL.json` based on the outcome matrix below.
129
127
 
130
128
  ## Outcome Matrix
@@ -134,7 +132,6 @@ Write `SIGNAL.json` only after step 15 is evaluated.
134
132
  - **Success**:
135
133
  - every required validation passed or was explicitly non-applicable by contract
136
134
  - no required proof target remains `UNRESOLVED`
137
- - `STATUS: done`
138
135
  - signal:
139
136
  ```json
140
137
  { "status": "complete", "outcome": "success" }
@@ -143,7 +140,6 @@ Write `SIGNAL.json` only after step 15 is evaluated.
143
140
  - **Blocked**:
144
141
  - a concrete environment/runtime/tooling blocker prevented required validation
145
142
  - or a proof target remains `UNRESOLVED` because a required live/runtime dependency was unavailable
146
- - `STATUS: blocked`
147
143
  - signal:
148
144
  ```json
149
145
  { "status": "blocked", "outcome": "partial", "reason": "<concrete blocker>" }
@@ -152,7 +148,6 @@ Write `SIGNAL.json` only after step 15 is evaluated.
152
148
  - **Failed**:
153
149
  - a required validation actually ran and failed
154
150
  - or the recipe/proof contradicts the PR claim
155
- - `STATUS: failed`
156
151
  - signal:
157
152
  ```json
158
153
  { "status": "failed", "outcome": "failure", "reason": "<concrete failure>" }
@@ -12,7 +12,7 @@ Use this overlay when the target project is a browser or web app and has no more
12
12
  Start with:
13
13
 
14
14
  ```bash
15
- rg --files | rg 'recipes?/.*\\.json$|flows?/.*\\.json$|validate-recipe\\.(js|mjs|cjs|ts)|validate-flow-schema\\.(js|mjs|cjs|ts)|playwright|cypress|vitest|agentic-toolkit\\.md'
15
+ rg --files | rg 'recipes?/.*\\.json$|validate-recipe\\.(js|mjs|cjs|ts)|playwright|cypress|vitest|agentic-toolkit\\.md'
16
16
  ```
17
17
 
18
18
  Then inspect package scripts for existing validation surfaces:
@@ -37,7 +37,6 @@ Look for:
37
37
  Use repo-local commands first. Common shapes include:
38
38
 
39
39
  ```bash
40
- node <validate-flow-schema.js> <recipe.json>
41
40
  node <validate-recipe.js> --recipe <recipe.json> --dry-run
42
41
  npm test -- --runInBand
43
42
  ```
@@ -12,7 +12,7 @@ Use this overlay when the target project is a mobile app and has no more specifi
12
12
  Start with:
13
13
 
14
14
  ```bash
15
- rg --files | rg 'recipes?/.*\\.json$|flows?/.*\\.json$|validate-recipe\\.(sh|js|mjs|cjs|ts)|detox|maestro|appium|testIds?|accessibility|agentic-toolkit\\.md'
15
+ rg --files | rg 'recipes?/.*\\.json$|validate-recipe\\.(sh|js|mjs|cjs|ts)|detox|maestro|appium|testIds?|accessibility|agentic-toolkit\\.md'
16
16
  ```
17
17
 
18
18
  Then inspect package scripts for local test and validation commands:
@@ -23,7 +23,7 @@ node -e "const p=require('./package.json'); console.log(p.scripts || {})"
23
23
 
24
24
  ## Cooking Rules
25
25
 
26
- - Prefer stable accessibility labels, test IDs, or existing mobile flows when they exist.
26
+ - Prefer stable accessibility labels, test IDs, or existing mobile automation when it exists.
27
27
  - Keep proof targets separate from platform mechanics.
28
28
  - Do not import web-only selectors or desktop assumptions into mobile recipes.
29
29
  - If mobile lacks a repo-owned helper, write a recipe and proof plan, then mark live validation unavailable with the concrete blocker.
@@ -16,7 +16,7 @@ Use the local recipe runtime when one exists, or recommend the smallest install
16
16
 
17
17
  1. Find the project-owned recipe command first.
18
18
  2. If no command exists, check for `@farmslot/recipe-harness` or an app-specific harness package.
19
- 3. Before authoring, inspect declared actions and reusable flows. Prefer project discovery commands. For generic flow discovery, run `farmslot-recipe flows list` and `farmslot-recipe flows describe <ref>`. If no action discovery command exists, read only the relevant project manifest entries and examples.
19
+ 3. Before authoring, inspect the closest recipes with `farmslot-recipe run --list` and `farmslot-recipe run <id> --describe`, then inspect declared actions only if no recipe fits. Prefer project discovery commands; otherwise read only the relevant manifest entries and examples.
20
20
  4. Run dry-run or schema validation before live execution when available.
21
21
  5. Record artifact paths and validation output.
22
22
  6. If no runner exists, stop with a concrete install recommendation.
@@ -25,7 +25,7 @@ Use the local recipe runtime when one exists, or recommend the smallest install
25
25
 
26
26
  - Do not invent runner commands.
27
27
  - Do not require Farmslot Gateway, pool files, slots, Command Center, or Companion for first use.
28
- - Do not bypass project fixtures or user-flow validation by directly mutating app state.
28
+ - Do not bypass project fixtures or user-journey validation by directly mutating app state.
29
29
 
30
30
  ## Output
31
31