@farmslot/skills 0.3.0 → 0.5.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 +10 -0
- package/README.md +29 -0
- package/package.json +2 -2
- package/scripts/run-cooking-lane.cjs +47 -17
- package/skills/recipe-cook/SKILL.md +2 -2
- package/skills/recipe-cook/TASK.md +2 -3
- package/skills/recipe-cook/references/TASK.md +3 -8
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to `@farmslot/skills` are tracked here.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
- Active-development baseline; add user-facing changes here before release or package publication.
|
|
8
|
+
|
|
9
|
+
## 0.5.0 - 2026-09-20
|
|
10
|
+
|
|
11
|
+
- README documents the checklist-shaped skill rule: a skill invoked as a child checklist unit (ADR-060) needs `##` sections and `- [ ]` rows the shared parser can enumerate, informational sections named so the skip list drops them, and numbered labels matching their positions. A prose-only skill cannot be a child unit.
|
|
12
|
+
|
|
13
|
+
## 0.4.0 - 2026-09-17
|
|
14
|
+
|
|
15
|
+
- 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.
|
|
16
|
+
|
|
7
17
|
## 0.3.0 - 2026-08-03
|
|
8
18
|
|
|
9
19
|
- Teach recipe authors to start from existing parameterized recipes and compose them node by node before adding new capabilities.
|
package/README.md
CHANGED
|
@@ -44,6 +44,35 @@ Supported layouts:
|
|
|
44
44
|
| `project-adopt` | Recommend the smallest useful Farmslot integration level for the project. |
|
|
45
45
|
| `packet` | Communicate plans, blockers, decisions, and evidence reviews as compact operator packets. |
|
|
46
46
|
|
|
47
|
+
## Checklist-shaped skills
|
|
48
|
+
|
|
49
|
+
A checklist step can hand its work to a skill as a **child unit**: the step registers the
|
|
50
|
+
skill, `mark` materializes its body as `subtasks/<id>.md` beside the task checklist, and the
|
|
51
|
+
worker marks the skill's own rows so progress is visible per row instead of one box open for
|
|
52
|
+
the whole review. Farmslot only materializes and observes files; nothing is spawned.
|
|
53
|
+
|
|
54
|
+
A skill can be a child unit when its body is enumerable by the same parser the task checklist
|
|
55
|
+
uses:
|
|
56
|
+
|
|
57
|
+
- `##` (or deeper) headings and `- [ ]` rows, with at least one row. A prose-only skill cannot be
|
|
58
|
+
a child unit.
|
|
59
|
+
- Name informational sections so the shared skip list drops them: `Rules`, `Description`,
|
|
60
|
+
`Task`, `Acceptance Criteria`, `Affected Area`, `Screenshots`, `Comments`, `Root Cause`,
|
|
61
|
+
`Recipe ACs`, `Pre-merge`. Boxes there are never steps.
|
|
62
|
+
- If a row label carries its own number (`**3. …**`), it must match the row's position. The
|
|
63
|
+
numbering guard refuses a body where they diverge, because `mark` targets positions.
|
|
64
|
+
|
|
65
|
+
A template step registers the installed skill and finishes it:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
{{TASK_DIR}}/mark sub start perps-review --step 21 --from .agents/skills/mms-perps-review-pr/skill.md
|
|
69
|
+
{{TASK_DIR}}/mark sub perps-review 1 # …one per row
|
|
70
|
+
{{TASK_DIR}}/mark sub perps-review complete --report artifacts/review.md
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The step's own box is ticked by that `complete`, so the template says what to register, and the
|
|
74
|
+
skill owns what to check.
|
|
75
|
+
|
|
47
76
|
## Adoption Ladder
|
|
48
77
|
|
|
49
78
|
1. Skills only: author and review recipes without a runner.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farmslot/skills",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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.
|
|
36
|
+
"@farmslot/agent-runtime": "0.13.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "^22.0.0",
|
|
@@ -429,7 +429,9 @@ function buildPrompt({
|
|
|
429
429
|
'',
|
|
430
430
|
'## Required output JSON schema',
|
|
431
431
|
'{',
|
|
432
|
-
' "task_markdown": "full TASK.md content based on the provided template.
|
|
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",',
|
|
433
435
|
' "recipe_json": { "valid": "json object" },',
|
|
434
436
|
' "recipe_cook_json": { "valid": "json object" } or null,',
|
|
435
437
|
' "evidence_verdict": "good|ok|bad",',
|
|
@@ -451,7 +453,8 @@ function buildPrompt({
|
|
|
451
453
|
'',
|
|
452
454
|
'The JSON object you return is the final serialized payload for those files.',
|
|
453
455
|
'Emit the JSON last, after you have fully reasoned through:',
|
|
454
|
-
'- the rewritten TASK.md contents
|
|
456
|
+
'- the rewritten TASK.md contents',
|
|
457
|
+
'- the terminal_status (done, blocked, or failed) and, for blocked or failed, its concrete terminal_reason',
|
|
455
458
|
'- the recipe artifact contents',
|
|
456
459
|
'- the learning artifact contents',
|
|
457
460
|
'- what validation commands should run',
|
|
@@ -769,26 +772,47 @@ function renderValidationEvidence(validationResults, cdpPort, repoRoot) {
|
|
|
769
772
|
return lines.join('\n');
|
|
770
773
|
}
|
|
771
774
|
|
|
772
|
-
|
|
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) {
|
|
773
779
|
const text = String(taskMarkdown || '');
|
|
774
|
-
|
|
775
|
-
|
|
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 };
|
|
776
792
|
}
|
|
777
|
-
if (
|
|
778
|
-
return { status: 'failed', outcome: 'failure' };
|
|
793
|
+
if (envelope.terminal_status === 'failed') {
|
|
794
|
+
return { status: 'failed', outcome: 'failure', reason };
|
|
779
795
|
}
|
|
780
796
|
if (/^\s*[-*]\s+.*:\s*UNRESOLVED\b/m.test(text)) {
|
|
781
|
-
return {
|
|
797
|
+
return {
|
|
798
|
+
status: 'blocked',
|
|
799
|
+
outcome: 'partial',
|
|
800
|
+
reason: 'a required proof target remains UNRESOLVED',
|
|
801
|
+
};
|
|
782
802
|
}
|
|
783
803
|
|
|
784
|
-
const failingStep = Object.
|
|
785
|
-
(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,
|
|
786
806
|
);
|
|
787
807
|
if (failingStep) {
|
|
788
|
-
return {
|
|
808
|
+
return {
|
|
809
|
+
status: 'failed',
|
|
810
|
+
outcome: 'failure',
|
|
811
|
+
reason: `validation step ${failingStep[0]} exited ${failingStep[1].exit_code}`,
|
|
812
|
+
};
|
|
789
813
|
}
|
|
790
814
|
|
|
791
|
-
return { status: 'done', outcome: 'success' };
|
|
815
|
+
return { status: 'done', outcome: 'success', reason: null };
|
|
792
816
|
}
|
|
793
817
|
|
|
794
818
|
function injectValidationEvidence(taskMarkdown, evidenceBlock) {
|
|
@@ -987,6 +1011,15 @@ async function main() {
|
|
|
987
1011
|
typeof envelope.next_delta === 'string' && envelope.next_delta.trim(),
|
|
988
1012
|
'Runner output missing next_delta',
|
|
989
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
|
+
);
|
|
990
1023
|
writeJson(path.join(runDir, 'runner-response.json'), envelope);
|
|
991
1024
|
|
|
992
1025
|
const recipePath = path.join(artifactsDir, 'recipe.json');
|
|
@@ -1009,11 +1042,7 @@ async function main() {
|
|
|
1009
1042
|
const evidenceBlock = renderValidationEvidence(validationResults, effectiveCdpPort, projectRoot);
|
|
1010
1043
|
const normalizedTask = normalizeTaskArtifactDir(envelope.task_markdown, artifactsDir);
|
|
1011
1044
|
let finalTask = injectValidationEvidence(normalizedTask, evidenceBlock);
|
|
1012
|
-
const terminalState = deriveTerminalState(finalTask, validationResults);
|
|
1013
|
-
finalTask = finalTask.replace(
|
|
1014
|
-
/STATUS:\s*(pending|working|done|blocked|failed)/i,
|
|
1015
|
-
`STATUS: ${terminalState.status}`,
|
|
1016
|
-
);
|
|
1045
|
+
const terminalState = deriveTerminalState(envelope, finalTask, validationResults);
|
|
1017
1046
|
writeFile(path.join(runDir, 'TASK.md'), finalTask);
|
|
1018
1047
|
|
|
1019
1048
|
const verdict = mapVerdict(envelope.evidence_verdict, validationResults);
|
|
@@ -1036,6 +1065,7 @@ async function main() {
|
|
|
1036
1065
|
: terminalState.outcome === 'partial'
|
|
1037
1066
|
? 'blocked'
|
|
1038
1067
|
: 'completed',
|
|
1068
|
+
terminal_reason: terminalState.reason,
|
|
1039
1069
|
});
|
|
1040
1070
|
writeJson(path.join(artifactsDir, 'grade.json'), {
|
|
1041
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
|
-
- `
|
|
96
|
+
- `terminal_status: done` in the run envelope; `blocked` and `failed` carry a concrete `terminal_reason`
|
|
97
97
|
|
|
98
98
|
## Hard Rules
|
|
99
99
|
|
|
@@ -119,7 +119,7 @@ Completion requires:
|
|
|
119
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
|
|
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
|
|
@@ -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
|
|
@@ -87,7 +86,7 @@ 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
|
|
89
|
+
- [ ] **1. Fill the task block** — set `TARGET_REPO`, `SOURCE_KIND`, `SOURCE_REF`, `ARTIFACT_DIR`, `VALIDATION_MODE`.
|
|
91
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.
|
|
@@ -99,7 +98,7 @@ It must include:
|
|
|
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** —
|
|
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.
|
|
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
|
|
@@ -110,7 +108,7 @@ 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
|
|
111
|
+
- [ ] **1. Fill the task block** — set `TARGET_REPO`, `SOURCE_KIND`, `SOURCE_REF`, `ARTIFACT_DIR`, `VALIDATION_MODE`.
|
|
114
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.
|
|
@@ -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** —
|
|
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>" }
|