@jaybeeuu/agent-cortex 1.39.0 → 1.41.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/.agents/skills/style-versioning/SKILL.md +1 -1
- package/.github/workflows/ci.yml +47 -14
- package/AGENTS.md +35 -8
- package/CHANGELOG.md +60 -0
- package/README.md +174 -97
- package/agents/README.md +14 -11
- package/bin/agent-cortex.mjs +55 -10
- package/bin/installers/claude.mjs +674 -174
- package/bin/installers/copilot.mjs +13 -18
- package/bin/installers/pi.mjs +293 -51
- package/claude/settings.json +4 -0
- package/{claude → claude-extras}/scripts/statusline-command.sh +32 -4
- package/docs/claude-hooks.md +15 -3
- package/docs/ideas/ban-content-assertion-tests.md +73 -0
- package/docs/ideas/bead-progress-notes-for-session-resumption.md +75 -0
- package/docs/ideas/classify-beads-via-deterministic-script-with-agent-fallback.md +70 -0
- package/docs/ideas/ctx-alias-version-and-update-commands.md +65 -0
- package/docs/ideas/ctx-feedback-command-for-capturing-ideas-from-any-machine.md +67 -0
- package/docs/ideas/deployment-beads-for-post-merge-pipelines.md +36 -0
- package/docs/ideas/design-and-delivery-as-separate-bead-trees/design-and-delivery-as-separate-bead-trees.md +99 -0
- package/docs/ideas/enable-signed-commit-enforcement-changesets-bot-commits-must-be-signed.md +33 -0
- package/docs/ideas/extension-manifests.md +55 -14
- package/docs/ideas/improve-pi-system-prompt.md +5 -2
- package/docs/ideas/map-every-cortex-tool-to-a-real-pi-equivalent.md +95 -0
- package/docs/ideas/record-idea-implementation-questions.md +68 -0
- package/docs/ideas/self-flushing-session.md +72 -0
- package/docs/ideas/state-in-beads-not-md.md +69 -0
- package/docs/ideas/task-session-context.local.md +95 -0
- package/docs/ideas/test-data-creation-helpers.md +92 -0
- package/docs/ideas/test-planning-stage-bead-in-feature-planning.md +68 -0
- package/docs/ideas/worktree-housekeeping-for-closed-beads.md +39 -0
- package/docs/technical-direction/harness-config.md +161 -0
- package/docs/technical-direction/migration-execution-labels.md +60 -23
- package/extensions/agent-guardrails/README.md +45 -0
- package/extensions/agent-guardrails/guardrails.test.ts +42 -0
- package/extensions/agent-guardrails/guardrails.ts +66 -0
- package/extensions/agent-guardrails/index.ts +22 -0
- package/extensions/agent-guardrails/node_modules/.bin/pi +21 -0
- package/extensions/agent-guardrails/node_modules/.bin/tsc +21 -0
- package/extensions/agent-guardrails/node_modules/.bin/tsserver +21 -0
- package/extensions/agent-guardrails/node_modules/.bin/tsx +21 -0
- package/extensions/agent-guardrails/package.json +19 -0
- package/extensions/agent-guardrails/tsconfig.json +12 -0
- package/extensions/agent-modes/README.md +12 -5
- package/extensions/agent-modes/discover.test.ts +30 -7
- package/extensions/agent-modes/discover.ts +1 -1
- package/extensions/agent-modes/index.test.ts +208 -0
- package/extensions/agent-modes/index.ts +23 -6
- package/extensions/agent-modes/package.json +1 -1
- package/extensions/skill-stats/index.test.ts +3 -2
- package/lib/cli.mjs +30 -12
- package/lib/pi-packages.mjs +169 -0
- package/package.json +8 -4
- package/pi/settings.json +23 -12
- package/plugin.json +1 -1
- package/scripts/build-copilot-agents.mjs +1 -1
- package/scripts/check-no-sync.mjs +49 -0
- package/scripts/lib/compose-agent.mjs +32 -14
- package/scripts/lib/fs.mjs +31 -0
- package/skills/engineering/grill-with-docs/SKILL.md +1 -1
- package/skills/planning/classify-bead/REFERENCE.md +85 -0
- package/skills/planning/classify-bead/SKILL.md +32 -36
- package/skills/planning/classify-bead/scripts/classify-bead.mjs +292 -0
- package/skills/planning/create-task/SKILL.md +19 -10
- package/skills/planning/write-a-prd/SKILL.md +2 -2
- package/skills/style/style-code/REFERENCE.md +32 -2
- package/skills/style/style-code/SKILL.md +5 -21
- package/skills/style/style-tests/SKILL.md +69 -17
- package/skills/workflow/hitl-collab/SKILL.md +1 -1
- package/skills/workflow/run-pipeline-stage/SKILL.md +13 -1
- package/skills/workflow/run-pipeline-stage/prompts/stage-runner.md +12 -0
- package/test/classify-bead.test.mjs +277 -0
- package/test/claude-installer.test.mjs +199 -139
- package/test/cli.test.mjs +95 -41
- package/test/copilot-installer.test.mjs +92 -82
- package/test/fs.test.mjs +76 -0
- package/test/helpers/fake-claude.mjs +152 -0
- package/test/installer.test.mjs +542 -57
- package/test/pi-installer.test.mjs +457 -102
- package/test/pi-packages.test.mjs +295 -0
- package/token-map.README.md +23 -12
- package/token-map.json +8 -7
- package/.claude-plugin/marketplace.json +0 -16
- package/claude/.claude-plugin/plugin.json +0 -11
- package/claude/agents/plan.md +0 -58
- package/claude/agents/ralph-plan.md +0 -121
- package/claude/agents/ralph.md +0 -193
- package/claude/agents/strategy.md +0 -111
- package/claude/hooks/scripts/notify.mjs +0 -54
- package/claude/hooks.json +0 -38
- /package/{claude → claude-extras}/.mcp.json +0 -0
|
@@ -80,7 +80,7 @@ Needs sign-off on the API contract change.
|
|
|
80
80
|
- [ ] Contract change approved by reviewer
|
|
81
81
|
## Manual steps
|
|
82
82
|
1. `pnpm install && pnpm test`
|
|
83
|
-
2. `
|
|
83
|
+
2. `agent-cortex install claude --dry-run` # materialiser smoke check (CI runs the full structural check)
|
|
84
84
|
## Verification checklist
|
|
85
85
|
- [ ] Tests pass locally
|
|
86
86
|
- [ ] Generated output up to date
|
|
@@ -24,7 +24,7 @@ Stage beads (`stage:code`, `stage:verify`, `stage:review`, `stage:document`, `st
|
|
|
24
24
|
|
|
25
25
|
1. Run `bd prime` and hold the output; subagents can run it themselves if they need context.
|
|
26
26
|
2. If no bead was specified, run `bd ready` and ask the user which to work on.
|
|
27
|
-
3. Claim the bead with `bd update <id> --claim` — the bead, not the prompt, is the source of truth.
|
|
27
|
+
3. Claim the bead with `bd update <id> --claim` — the bead, not the prompt, is the source of truth. Append a stage-start note immediately (see _Milestone Notes_).
|
|
28
28
|
4. Read the bead's `stage:*` label — it names the stage to execute.
|
|
29
29
|
5. Load the universal stage-runner prompt (`skills/workflow/run-pipeline-stage/prompts/stage-runner.md`), populate `<stage>`, and read the matching playbook (`skills/workflow/run-pipeline-stage/playbooks/<stage>.md`) for stage-specific rules.
|
|
30
30
|
6. Tag the stage and regenerate the progress doc so pairing sessions stay current:
|
|
@@ -88,6 +88,16 @@ Log stage starts and completes, test/lint/build/security results, blockers, and
|
|
|
88
88
|
mkdir -p .agent-cortex/ralph && echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [bead-id] [stage] message" >> .agent-cortex/ralph/ralph-bead-id.log
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
## Milestone Notes
|
|
92
|
+
|
|
93
|
+
The bead's notes are the recovery contract: if a session dies mid-stage, a fresh agent must resume from `bd show <id>` alone. Append a short checkpoint at stage start and at each milestone — never on every tool call.
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
bd update <id> --append-notes "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [<stage>] <what changed / what's next>"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`--append-notes` preserves the trail; plain `--notes` overwrites it. Record the start (stage, worktree, first action), milestones (completed chunks, tests green, files changed), decisions and rejected options, blocks, and discoveries. One or two lines per entry — a scan-friendly checkpoint trail, not a transcript. No new scratch files; beads stay the single source of truth.
|
|
100
|
+
|
|
91
101
|
## Report Format
|
|
92
102
|
|
|
93
103
|
Every subagent prompt must end with the REPORT instruction:
|
|
@@ -113,6 +123,7 @@ Subagents report facts — do not ask them to suggest or predict the next step.
|
|
|
113
123
|
- Composing a subagent prompt without the REPORT contract — the output cannot be routed.
|
|
114
124
|
- Creating a feedback bead and then dispatching anyway — bypasses the fix-round accounting and parent-child dependency graph.
|
|
115
125
|
- Skipping the playbook "because the stage is simple" — playbooks carry the per-stage discipline the pipeline relies on.
|
|
126
|
+
- Leaving bead notes to the end of the stage — a session that dies mid-stage then loses the whole trail.
|
|
116
127
|
|
|
117
128
|
## Common Rationalizations
|
|
118
129
|
|
|
@@ -145,5 +156,6 @@ Subagents report facts — do not ask them to suggest or predict the next step.
|
|
|
145
156
|
- [ ] Correct playbook read and followed for the stage; stage skills invoked where the playbook says.
|
|
146
157
|
- [ ] Composed prompt ends with the complete REPORT contract block.
|
|
147
158
|
- [ ] Progress log lines written for every stage transition and key result.
|
|
159
|
+
- [ ] Milestone notes appended at stage start and each milestone with `--append-notes` (not `--notes`).
|
|
148
160
|
- [ ] REPORT routed per the Dispatch Rules — next stage, feedback bead, or `bd close <id>`.
|
|
149
161
|
- [ ] Feedback beads carry the full BLOCKING_ISSUES content and the parent-child dependency.
|
|
@@ -14,6 +14,18 @@ Run `bd prime` if you need project-level context (conventions, tech stack, goals
|
|
|
14
14
|
## Progress log
|
|
15
15
|
Write progress to: `.agent-cortex/ralph/ralph-<bead-id>.log`
|
|
16
16
|
|
|
17
|
+
## Milestone notes
|
|
18
|
+
The bead's notes are the recovery contract — a fresh session must be able to resume from
|
|
19
|
+
`bd show <id>` alone. Append a short checkpoint before starting and at each milestone:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
bd update <id> --append-notes "[<ISO-timestamp>] [<stage>] <what changed / what's next>"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Use `--append-notes` — plain `--notes` overwrites the trail. Record start, milestones, decisions
|
|
26
|
+
(with rejected options), blocks, and discoveries. One or two lines per entry: a checkpoint trail,
|
|
27
|
+
not a transcript. The REPORT below remains the authoritative stage outcome.
|
|
28
|
+
|
|
17
29
|
## Instructions
|
|
18
30
|
1. Read and follow the playbook file listed above.
|
|
19
31
|
2. Treat the bead as the source-of-truth specification (not a prompt script).
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { access } from 'node:fs/promises';
|
|
4
|
+
import { dirname, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
import { resolveClassification, auditBeads, runCli } from '../skills/planning/classify-bead/scripts/classify-bead.mjs';
|
|
8
|
+
|
|
9
|
+
const testDir = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
describe('resolveClassification', () => {
|
|
12
|
+
it('resolves AFK from an existing implementation-type:afk label', () => {
|
|
13
|
+
const result = resolveClassification({
|
|
14
|
+
labels: ['implementation-type:afk'],
|
|
15
|
+
description: 'Do a thing.',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
assert.equal(result.classification, 'afk');
|
|
19
|
+
assert.equal(result.source, 'label');
|
|
20
|
+
assert.equal(result.escalate, false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('resolves HITL from an existing implementation-type:hitl label', () => {
|
|
24
|
+
const result = resolveClassification({
|
|
25
|
+
labels: ['implementation-type:hitl'],
|
|
26
|
+
description: 'Provision the staging cluster.',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
assert.equal(result.classification, 'hitl');
|
|
30
|
+
assert.equal(result.source, 'label');
|
|
31
|
+
assert.equal(result.escalate, false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('prefers AFK when both implementation-type labels are present', () => {
|
|
35
|
+
const result = resolveClassification({
|
|
36
|
+
labels: ['implementation-type:hitl', 'implementation-type:afk'],
|
|
37
|
+
description: 'Ambiguous bead.',
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
assert.equal(result.classification, 'afk');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('resolves AFK from a legacy ## Type block when no label is present', () => {
|
|
44
|
+
const result = resolveClassification({
|
|
45
|
+
labels: [],
|
|
46
|
+
description: 'Add the export API.\n\n## Type\n\nAFK\n\n## Design\n\nSomething.',
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
assert.equal(result.classification, 'afk');
|
|
50
|
+
assert.equal(result.source, 'type-field');
|
|
51
|
+
assert.equal(result.escalate, false);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('resolves HITL from a case-insensitive ## type block', () => {
|
|
55
|
+
const result = resolveClassification({
|
|
56
|
+
labels: [],
|
|
57
|
+
description: 'Rotate the signing key.\n\n## type\n\nhitl',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
assert.equal(result.classification, 'hitl');
|
|
61
|
+
assert.equal(result.source, 'type-field');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('resolves an inline ## Type: AFK field', () => {
|
|
65
|
+
const result = resolveClassification({
|
|
66
|
+
labels: [],
|
|
67
|
+
description: 'Tidy the logs.\n\n## Type: AFK\n',
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
assert.equal(result.classification, 'afk');
|
|
71
|
+
assert.equal(result.source, 'type-field');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('prefers the label over a conflicting ## Type field', () => {
|
|
75
|
+
const result = resolveClassification({
|
|
76
|
+
labels: ['implementation-type:afk'],
|
|
77
|
+
description: 'Conflicting bead.\n\n## Type\n\nHITL',
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
assert.equal(result.classification, 'afk');
|
|
81
|
+
assert.equal(result.source, 'label');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('escalates when the ## Type field carries no AFK/HITL value', () => {
|
|
85
|
+
const result = resolveClassification({
|
|
86
|
+
labels: [],
|
|
87
|
+
description: 'A feature.\n\n## Type\n\nFeature',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
assert.equal(result.classification, null);
|
|
91
|
+
assert.equal(result.escalate, true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('resolves AFK from explicit no-human phrasing', () => {
|
|
95
|
+
const result = resolveClassification({
|
|
96
|
+
labels: [],
|
|
97
|
+
description: 'Rename the column; no human review required.',
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
assert.equal(result.classification, 'afk');
|
|
101
|
+
assert.equal(result.source, 'heuristic');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('resolves HITL from an explicit human sign-off requirement', () => {
|
|
105
|
+
const result = resolveClassification({
|
|
106
|
+
labels: [],
|
|
107
|
+
description: 'Ship the redesign once it has stakeholder sign-off.',
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
assert.equal(result.classification, 'hitl');
|
|
111
|
+
assert.equal(result.source, 'heuristic');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('lets a HITL signal outrank a generic automated-tests signal', () => {
|
|
115
|
+
const result = resolveClassification({
|
|
116
|
+
labels: [],
|
|
117
|
+
description: 'Has automated tests, but the release requires human approval.',
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
assert.equal(result.classification, 'hitl');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('resolves AFK when the acceptance criteria are automated tests', () => {
|
|
124
|
+
const result = resolveClassification({
|
|
125
|
+
labels: [],
|
|
126
|
+
description: 'Cover the parser with automated tests.',
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
assert.equal(result.classification, 'afk');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('escalates when no deterministic signal is present', () => {
|
|
133
|
+
const result = resolveClassification({
|
|
134
|
+
labels: [],
|
|
135
|
+
description: 'Rework the billing domain model to support proration.',
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
assert.equal(result.classification, null);
|
|
139
|
+
assert.equal(result.source, null);
|
|
140
|
+
assert.equal(result.escalate, true);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('prefers the ## Type field over heuristic signals', () => {
|
|
144
|
+
const result = resolveClassification({
|
|
145
|
+
labels: [],
|
|
146
|
+
description: '## Type\n\nHITL\n\nCover it with automated tests.',
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
assert.equal(result.classification, 'hitl');
|
|
150
|
+
assert.equal(result.source, 'type-field');
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe('auditBeads', () => {
|
|
155
|
+
const sample = [
|
|
156
|
+
{ labels: ['implementation-type:afk'], description: '' },
|
|
157
|
+
{ labels: ['implementation-type:hitl'], description: '' },
|
|
158
|
+
{ labels: [], description: '## Type\n\nAFK' },
|
|
159
|
+
{ labels: [], description: 'Rework the billing domain model.' },
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
it('counts each resolution source and the deterministic share', () => {
|
|
163
|
+
const summary = auditBeads(sample);
|
|
164
|
+
|
|
165
|
+
assert.deepEqual(summary.bySource, { label: 2, typeField: 1, heuristic: 0, rubric: 1 });
|
|
166
|
+
assert.equal(summary.deterministic, 3);
|
|
167
|
+
assert.equal(summary.needsRubric, 1);
|
|
168
|
+
assert.equal(summary.deterministicRatio, 0.75);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('reports a zero ratio for an empty sample', () => {
|
|
172
|
+
assert.equal(auditBeads([]).deterministicRatio, 0);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe('runCli', () => {
|
|
177
|
+
function makeExec(beads) {
|
|
178
|
+
const calls = [];
|
|
179
|
+
const exec = async (args) => {
|
|
180
|
+
calls.push(args);
|
|
181
|
+
if (args[0] === 'show') return JSON.stringify(beads);
|
|
182
|
+
if (args[0] === 'list') return JSON.stringify(beads);
|
|
183
|
+
if (args[0] === 'tag') return '';
|
|
184
|
+
throw new Error(`unexpected bd call: ${args.join(' ')}`);
|
|
185
|
+
};
|
|
186
|
+
return { exec, calls };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function capture() {
|
|
190
|
+
let output = '';
|
|
191
|
+
return { write: (chunk) => { output += chunk; }, read: () => JSON.parse(output) };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
it('applies the label when the ## Type field resolves the classification', async () => {
|
|
195
|
+
const { exec, calls } = makeExec([{ id: 'proj-1', labels: [], description: '## Type\n\nAFK' }]);
|
|
196
|
+
const out = capture();
|
|
197
|
+
|
|
198
|
+
const code = await runCli({ argv: ['proj-1'], exec, write: out.write });
|
|
199
|
+
|
|
200
|
+
assert.equal(code, 0);
|
|
201
|
+
assert.deepEqual(calls, [
|
|
202
|
+
['show', 'proj-1', '--json'],
|
|
203
|
+
['tag', 'proj-1', 'implementation-type:afk'],
|
|
204
|
+
]);
|
|
205
|
+
assert.deepEqual(out.read(), {
|
|
206
|
+
id: 'proj-1',
|
|
207
|
+
classification: 'afk',
|
|
208
|
+
source: 'type-field',
|
|
209
|
+
reason: 'legacy ## Type field declares AFK',
|
|
210
|
+
escalate: false,
|
|
211
|
+
applied: true,
|
|
212
|
+
label: 'implementation-type:afk',
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('does not re-apply a label that is already present', async () => {
|
|
217
|
+
const { exec, calls } = makeExec([{ id: 'proj-2', labels: ['implementation-type:hitl'], description: '' }]);
|
|
218
|
+
const out = capture();
|
|
219
|
+
|
|
220
|
+
const code = await runCli({ argv: ['proj-2'], exec, write: out.write });
|
|
221
|
+
|
|
222
|
+
assert.equal(code, 0);
|
|
223
|
+
assert.deepEqual(calls, [['show', 'proj-2', '--json']]);
|
|
224
|
+
assert.equal(out.read().applied, false);
|
|
225
|
+
assert.equal(out.read().classification, 'hitl');
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('escalates without applying a label when no deterministic signal is present', async () => {
|
|
229
|
+
const { exec, calls } = makeExec([{ id: 'proj-3', labels: [], description: 'Rework the billing model.' }]);
|
|
230
|
+
const out = capture();
|
|
231
|
+
|
|
232
|
+
const code = await runCli({ argv: ['proj-3'], exec, write: out.write });
|
|
233
|
+
|
|
234
|
+
assert.equal(code, 0);
|
|
235
|
+
assert.deepEqual(calls, [['show', 'proj-3', '--json']]);
|
|
236
|
+
assert.equal(out.read().escalate, true);
|
|
237
|
+
assert.equal(out.read().label, null);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('resolves without applying a label under --dry-run', async () => {
|
|
241
|
+
const { exec, calls } = makeExec([{ id: 'proj-4', labels: [], description: '## Type\n\nHITL' }]);
|
|
242
|
+
const out = capture();
|
|
243
|
+
|
|
244
|
+
const code = await runCli({ argv: ['proj-4', '--dry-run'], exec, write: out.write });
|
|
245
|
+
|
|
246
|
+
assert.equal(code, 0);
|
|
247
|
+
assert.deepEqual(calls, [['show', 'proj-4', '--json']]);
|
|
248
|
+
assert.equal(out.read().classification, 'hitl');
|
|
249
|
+
assert.equal(out.read().applied, false);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('reports the deterministic split in --audit mode', async () => {
|
|
253
|
+
const beads = [
|
|
254
|
+
{ labels: ['implementation-type:afk'], description: '' },
|
|
255
|
+
{ labels: [], description: 'Rework the billing model.' },
|
|
256
|
+
];
|
|
257
|
+
const { exec, calls } = makeExec(beads);
|
|
258
|
+
const out = capture();
|
|
259
|
+
|
|
260
|
+
const code = await runCli({ argv: ['--audit', '--limit', '2'], exec, write: out.write });
|
|
261
|
+
|
|
262
|
+
assert.equal(code, 0);
|
|
263
|
+
assert.deepEqual(calls, [['list', '--all', '--json', '--limit', '2', '--sort', 'created']]);
|
|
264
|
+
assert.equal(out.read().deterministic, 1);
|
|
265
|
+
assert.equal(out.read().needsRubric, 1);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe('classifier wiring', () => {
|
|
270
|
+
it('exposes the classifier at the sibling path callers are told to use', async () => {
|
|
271
|
+
// create-task/SKILL.md resolves <skill-scripts>/../../classify-bead/scripts/classify-bead.mjs.
|
|
272
|
+
const createTaskScripts = resolve(testDir, '../skills/planning/create-task/scripts');
|
|
273
|
+
const classifier = resolve(createTaskScripts, '../../classify-bead/scripts/classify-bead.mjs');
|
|
274
|
+
|
|
275
|
+
await assert.doesNotReject(access(classifier));
|
|
276
|
+
});
|
|
277
|
+
});
|