@besales/ops-framework 0.1.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 +328 -0
- package/bin/build-check-context.mjs +67 -0
- package/bin/build-execution-ledger.mjs +54 -0
- package/bin/estimate-llm-input.mjs +160 -0
- package/bin/guard-task.mjs +384 -0
- package/bin/hash-task-artifacts.mjs +44 -0
- package/bin/init-project.mjs +49 -0
- package/bin/intake-execution-feedback.mjs +207 -0
- package/bin/intake-feedback.test.mjs +73 -0
- package/bin/learning-loop.mjs +658 -0
- package/bin/learning-loop.test.mjs +175 -0
- package/bin/lib/bootstrap-utils.mjs +542 -0
- package/bin/lib/bootstrap-utils.test.mjs +156 -0
- package/bin/lib/check-context-utils.mjs +1448 -0
- package/bin/lib/check-context-utils.test.mjs +497 -0
- package/bin/lib/execution-ledger-utils.mjs +162 -0
- package/bin/lib/execution-ledger-utils.test.mjs +74 -0
- package/bin/lib/llm-input-pack-utils.mjs +663 -0
- package/bin/lib/llm-input-pack-utils.test.mjs +262 -0
- package/bin/lib/project-config.mjs +229 -0
- package/bin/lib/project-config.test.mjs +102 -0
- package/bin/lib/task-manifest-utils.mjs +512 -0
- package/bin/lib/task-manifest-utils.test.mjs +218 -0
- package/bin/lib/task-metrics-utils.mjs +63 -0
- package/bin/lib/task-metrics-utils.test.mjs +40 -0
- package/bin/lib/test-setup.mjs +37 -0
- package/bin/new-task.mjs +42 -0
- package/bin/ops-agent.mjs +81 -0
- package/bin/preflight.mjs +56 -0
- package/bin/providers/external-cli-checker.mjs +190 -0
- package/bin/providers/openai-checker.mjs +62 -0
- package/bin/quality-gates.mjs +92 -0
- package/bin/run-check.mjs +559 -0
- package/bin/run-plan-check-loop.mjs +392 -0
- package/bin/run-verify.mjs +627 -0
- package/bin/self-lint.mjs +88 -0
- package/bin/supervisor-turn.mjs +146 -0
- package/bin/supervisor-turn.test.mjs +72 -0
- package/bin/task-manifest.mjs +57 -0
- package/bin/task-metrics.mjs +48 -0
- package/bin/transition.mjs +94 -0
- package/bin/validate-check-artifacts.mjs +418 -0
- package/config/default-agents.json +100 -0
- package/package.json +28 -0
- package/playbooks/checker-context.md +9 -0
- package/playbooks/complexity-performance.md +13 -0
- package/playbooks/production-rollout.md +9 -0
- package/playbooks/source-sync-provider.md +9 -0
- package/playbooks/ui-acceptance.md +9 -0
- package/prompts/checker.md +170 -0
- package/prompts/executor.md +54 -0
- package/prompts/planner.md +128 -0
- package/prompts/researcher.md +44 -0
- package/prompts/supervisor.md +337 -0
- package/prompts/verifier.md +128 -0
- package/templates/brief.md +15 -0
- package/templates/check-resolution.md +69 -0
- package/templates/check-result.json +32 -0
- package/templates/check.md +46 -0
- package/templates/execution-feedback.md +25 -0
- package/templates/execution.md +101 -0
- package/templates/human-gate-summary.md +49 -0
- package/templates/orchestration-log.md +8 -0
- package/templates/plan.md +86 -0
- package/templates/research.md +13 -0
- package/templates/retrospective.md +48 -0
- package/templates/status.md +53 -0
- package/templates/verify-result.json +19 -0
- package/templates/verify.md +41 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import {
|
|
6
|
+
buildLearningCard,
|
|
7
|
+
collectLearningCandidates,
|
|
8
|
+
learningReasonHash,
|
|
9
|
+
parseLearningCandidatesMarkdown,
|
|
10
|
+
writeLearningReview,
|
|
11
|
+
writeLearningReport,
|
|
12
|
+
} from './learning-loop.mjs';
|
|
13
|
+
|
|
14
|
+
describe('learning loop', () => {
|
|
15
|
+
it('collects deduplicated candidates with source refs, hashes and suggested targets', () => {
|
|
16
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ops-learning-loop-'));
|
|
17
|
+
const taskDir = path.join(root, 'TASK-999-learning');
|
|
18
|
+
fs.mkdirSync(taskDir, { recursive: true });
|
|
19
|
+
fs.writeFileSync(path.join(taskDir, 'check.md'), [
|
|
20
|
+
'# Check',
|
|
21
|
+
'',
|
|
22
|
+
'- Classification: learning_capture',
|
|
23
|
+
'- Requires new Human Gate: no',
|
|
24
|
+
'- "rawFeedback": "UI acceptance playbook should include duplicated JSON field."',
|
|
25
|
+
'- UI acceptance playbook should include the billing route smoke scenario.',
|
|
26
|
+
'- UI acceptance playbook should include the billing route smoke scenario.',
|
|
27
|
+
'- Remember this project-specific glossary naming rule in memory.',
|
|
28
|
+
].join('\n'));
|
|
29
|
+
|
|
30
|
+
const candidates = collectLearningCandidates({ tasksRoot: root, limit: 10 });
|
|
31
|
+
|
|
32
|
+
expect(candidates).toHaveLength(2);
|
|
33
|
+
expect(candidates[0]).toMatchObject({
|
|
34
|
+
source: 'TASK-999-learning/check.md',
|
|
35
|
+
sourceArtifact: 'TASK-999-learning/check.md',
|
|
36
|
+
kind: 'project-playbook-candidate',
|
|
37
|
+
learningLayer: 'project-playbook-overlay',
|
|
38
|
+
confidence: 'medium',
|
|
39
|
+
problem: 'A reusable procedure or checklist gap was identified.',
|
|
40
|
+
suggestedTarget: 'project-playbook/ui-acceptance.md',
|
|
41
|
+
});
|
|
42
|
+
expect(candidates[0].reasonHash).toBe(learningReasonHash(candidates[0].text));
|
|
43
|
+
expect(candidates[0].proposedWording).toContain('Project playbook rule:');
|
|
44
|
+
expect(candidates[1].kind).toBe('project-memory-candidate');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('builds structured learning cards with repeat-risk and proposed wording', () => {
|
|
48
|
+
const text = 'Optimization gate must prevent repeated N+1 implementation before execute.';
|
|
49
|
+
const card = buildLearningCard({
|
|
50
|
+
source: 'TASK-001/verify.md',
|
|
51
|
+
sourceArtifact: 'TASK-001/verify.md',
|
|
52
|
+
reasonHash: learningReasonHash(text),
|
|
53
|
+
text,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
expect(card).toMatchObject({
|
|
57
|
+
kind: 'project-memory-candidate',
|
|
58
|
+
learningLayer: 'project-memory',
|
|
59
|
+
confidence: 'high',
|
|
60
|
+
repeatRisk: 'high: likely to repeat or cause a gate failure if not captured.',
|
|
61
|
+
});
|
|
62
|
+
expect(card.lesson).toContain('bounded optimization review');
|
|
63
|
+
expect(card.proposedWording).toContain(text);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('parses candidate markdown into learning-index entries without full artifact text', () => {
|
|
67
|
+
const entries = parseLearningCandidatesMarkdown([
|
|
68
|
+
'# Learning Candidates',
|
|
69
|
+
'',
|
|
70
|
+
'## LC-001',
|
|
71
|
+
'',
|
|
72
|
+
'- Source: `TASK-001/check.md`',
|
|
73
|
+
'- Source artifact: `TASK-001/check.md`',
|
|
74
|
+
'- Reason hash: `abc123`',
|
|
75
|
+
'- Kind: `project-playbook-candidate`',
|
|
76
|
+
'- Learning layer: `project-playbook-overlay`',
|
|
77
|
+
'- Confidence: `high`',
|
|
78
|
+
'- Problem: Repeated UI route gap.',
|
|
79
|
+
'- Lesson: Add route-specific UI acceptance.',
|
|
80
|
+
'- Repeat risk: high.',
|
|
81
|
+
'- Candidate: UI acceptance playbook should cover settings routes.',
|
|
82
|
+
'- Proposed wording: Project playbook rule: cover settings routes.',
|
|
83
|
+
'- Suggested target: `project-playbook/ui-acceptance.md`',
|
|
84
|
+
].join('\n'));
|
|
85
|
+
|
|
86
|
+
expect(entries).toEqual([
|
|
87
|
+
{
|
|
88
|
+
id: 'LC-001',
|
|
89
|
+
source: 'TASK-001/check.md',
|
|
90
|
+
sourceArtifact: 'TASK-001/check.md',
|
|
91
|
+
reasonHash: 'abc123',
|
|
92
|
+
kind: 'project-playbook-candidate',
|
|
93
|
+
learningLayer: 'project-playbook-overlay',
|
|
94
|
+
confidence: 'high',
|
|
95
|
+
problem: 'Repeated UI route gap.',
|
|
96
|
+
lesson: 'Add route-specific UI acceptance.',
|
|
97
|
+
repeatRisk: 'high.',
|
|
98
|
+
text: 'UI acceptance playbook should cover settings routes.',
|
|
99
|
+
proposedWording: 'Project playbook rule: cover settings routes.',
|
|
100
|
+
suggestedTarget: 'project-playbook/ui-acceptance.md',
|
|
101
|
+
},
|
|
102
|
+
]);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('writes a visible learning report from the current learning index', () => {
|
|
106
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ops-learning-report-'));
|
|
107
|
+
const memoryRoot = path.join(root, 'ops', 'agent-pipeline', 'memory');
|
|
108
|
+
fs.mkdirSync(memoryRoot, { recursive: true });
|
|
109
|
+
fs.writeFileSync(path.join(root, 'ops', 'project.ops.yaml'), [
|
|
110
|
+
'name: TestProject',
|
|
111
|
+
'ops:',
|
|
112
|
+
' memoryDir: ops/agent-pipeline/memory',
|
|
113
|
+
' tasksDir: ops/agent-pipeline/tasks',
|
|
114
|
+
' playbooksDir: ops/agent-pipeline/playbooks',
|
|
115
|
+
].join('\n'));
|
|
116
|
+
fs.writeFileSync(path.join(memoryRoot, 'learning-index.json'), JSON.stringify({
|
|
117
|
+
schemaVersion: 1,
|
|
118
|
+
entries: [
|
|
119
|
+
{
|
|
120
|
+
id: 'LC-001',
|
|
121
|
+
source: 'TASK-001/check.md',
|
|
122
|
+
sourceArtifact: 'TASK-001/check.md',
|
|
123
|
+
kind: 'project-playbook-candidate',
|
|
124
|
+
confidence: 'high',
|
|
125
|
+
problem: 'Repeated UI route gap.',
|
|
126
|
+
lesson: 'Add route-specific UI acceptance.',
|
|
127
|
+
candidate: 'UI acceptance playbook should cover settings routes.',
|
|
128
|
+
proposedWording: 'Project playbook rule: cover settings routes.',
|
|
129
|
+
decision: 'promote',
|
|
130
|
+
target: 'project-playbook/ui-acceptance.md',
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
}, null, 2));
|
|
134
|
+
|
|
135
|
+
writeLearningReport({ memoryRoot, projectRoot: root });
|
|
136
|
+
|
|
137
|
+
const report = fs.readFileSync(path.join(memoryRoot, 'learning-report.md'), 'utf8');
|
|
138
|
+
expect(report).toContain('## Summary');
|
|
139
|
+
expect(report).toContain('Promoted entries: 1');
|
|
140
|
+
expect(report).toContain('Project playbook rule: cover settings routes.');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('writes a human approval pack for pending learning decisions', () => {
|
|
144
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ops-learning-review-'));
|
|
145
|
+
const memoryRoot = path.join(root, 'ops', 'agent-pipeline', 'memory');
|
|
146
|
+
fs.mkdirSync(memoryRoot, { recursive: true });
|
|
147
|
+
fs.writeFileSync(path.join(memoryRoot, 'learning-index.json'), JSON.stringify({
|
|
148
|
+
schemaVersion: 1,
|
|
149
|
+
entries: [
|
|
150
|
+
{
|
|
151
|
+
id: 'LC-001',
|
|
152
|
+
source: 'TASK-001/feedback.md',
|
|
153
|
+
sourceArtifact: 'TASK-001/feedback.md',
|
|
154
|
+
kind: 'project-memory-candidate',
|
|
155
|
+
learningLayer: 'project-memory',
|
|
156
|
+
confidence: 'high',
|
|
157
|
+
repeatRisk: 'high: likely to repeat.',
|
|
158
|
+
problem: 'Feedback was not captured.',
|
|
159
|
+
lesson: 'Capture feedback at every stage.',
|
|
160
|
+
candidate: 'Feedback must be captured.',
|
|
161
|
+
proposedWording: 'Project memory note: capture feedback at every stage.',
|
|
162
|
+
decision: 'pending',
|
|
163
|
+
target: 'memory/approved-learning.md',
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
}, null, 2));
|
|
167
|
+
|
|
168
|
+
writeLearningReview({ memoryRoot, projectRoot: root });
|
|
169
|
+
|
|
170
|
+
const review = fs.readFileSync(path.join(memoryRoot, 'learning-review.md'), 'utf8');
|
|
171
|
+
expect(review).toContain('## Human Approval Contract');
|
|
172
|
+
expect(review).toContain('Decision to set in `learning-index.json`: `promote | defer | reject | rewrite`');
|
|
173
|
+
expect(review).toContain('Project memory note: capture feedback at every stage.');
|
|
174
|
+
});
|
|
175
|
+
});
|