@dtlabs/ai-toolkit 0.8.0 → 0.9.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/.claude/workflows/pm-phase1.js +22 -13
- package/.claude/workflows/pm-phase2.js +18 -7
- package/.claude/workflows/pm-phase3.js +57 -21
- package/CHANGELOG.md +15 -0
- package/package.json +1 -1
|
@@ -13,18 +13,30 @@ export const meta = {
|
|
|
13
13
|
|
|
14
14
|
async function appendLedgerEntry(featureDir, prefix, entry) {
|
|
15
15
|
const ledgerPath = `${featureDir}/${prefix}-token-ledger.json`
|
|
16
|
-
|
|
16
|
+
// started_at is generated via Bash inside the agent to get a real UTC timestamp with time
|
|
17
|
+
const entryWithoutTs = JSON.stringify({ ...entry, started_at: '__TS__', completed_at: null })
|
|
17
18
|
await agent(
|
|
18
|
-
`Append a JSON object to the ledger array at: ${ledgerPath}\n\
|
|
19
|
+
`Append a JSON object to the ledger array at: ${ledgerPath}\n\n` +
|
|
20
|
+
`1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
|
|
21
|
+
`2. Read the file. If it does not exist or cannot be parsed as a JSON array, start with [].\n` +
|
|
22
|
+
`3. Push this object onto the array, replacing "__TS__" in started_at with NOW: ${entryWithoutTs}\n` +
|
|
23
|
+
`4. Write the full array back (JSON, 2-space indent). Return no output.`,
|
|
19
24
|
{ label: 'append-ledger', phase: 'Requirements', model: 'haiku' }
|
|
20
25
|
)
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
async function updateLedgerEntry(featureDir, prefix, agentKey, updates) {
|
|
24
29
|
const ledgerPath = `${featureDir}/${prefix}-token-ledger.json`
|
|
25
|
-
|
|
30
|
+
// completed_at is generated via Bash inside the agent to get a real UTC timestamp with time
|
|
31
|
+
const updatesWithoutTs = JSON.stringify({ ...updates, completed_at: '__TS__' })
|
|
26
32
|
await agent(
|
|
27
|
-
`Update an entry in the ledger array at: ${ledgerPath}\n\
|
|
33
|
+
`Update an entry in the ledger array at: ${ledgerPath}\n\n` +
|
|
34
|
+
`1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
|
|
35
|
+
`2. Read the file. If it does not exist or cannot be parsed as a JSON array, do nothing.\n` +
|
|
36
|
+
`3. Search from the end for the last entry where agent === "${agentKey}".\n` +
|
|
37
|
+
`4. If found, merge these fields into that entry, replacing "__TS__" in completed_at with NOW: ${updatesWithoutTs}\n` +
|
|
38
|
+
`5. Write the full array back (JSON, 2-space indent). Return no output.\n` +
|
|
39
|
+
`6. If not found, do nothing.`,
|
|
28
40
|
{ label: 'update-ledger', phase: 'Requirements', model: 'haiku' }
|
|
29
41
|
)
|
|
30
42
|
}
|
|
@@ -108,14 +120,13 @@ const errors = []
|
|
|
108
120
|
|
|
109
121
|
if (discoveryResult.needs_requirements) {
|
|
110
122
|
const reqKey = 'generate-requirements:phase1'
|
|
111
|
-
const reqStartedAt = new Date().toISOString()
|
|
112
123
|
await appendLedgerEntry(feature_dir, prefix, {
|
|
113
124
|
agent: reqKey,
|
|
114
125
|
phase: 'phase1',
|
|
115
126
|
model: 'haiku',
|
|
116
127
|
status: 'running',
|
|
117
128
|
phase_delta_tokens: 0,
|
|
118
|
-
started_at:
|
|
129
|
+
started_at: '__TS__',
|
|
119
130
|
completed_at: null,
|
|
120
131
|
})
|
|
121
132
|
const beforeReq = budget.spent()
|
|
@@ -123,7 +134,7 @@ if (discoveryResult.needs_requirements) {
|
|
|
123
134
|
const reqTokens = budget.spent() - beforeReq
|
|
124
135
|
await updateLedgerEntry(feature_dir, prefix, reqKey, {
|
|
125
136
|
status: 'done',
|
|
126
|
-
completed_at:
|
|
137
|
+
completed_at: '__TS__',
|
|
127
138
|
phase_delta_tokens: reqTokens,
|
|
128
139
|
})
|
|
129
140
|
tokenLedger.push({ agent: 'generate-requirements', model: 'haiku', phase_delta_tokens: reqTokens })
|
|
@@ -137,14 +148,13 @@ phase('Tech-Spec')
|
|
|
137
148
|
|
|
138
149
|
if (discoveryResult.needs_tech_spec) {
|
|
139
150
|
const specKey = 'generate-tech-spec:phase1'
|
|
140
|
-
const specStartedAt = new Date().toISOString()
|
|
141
151
|
await appendLedgerEntry(feature_dir, prefix, {
|
|
142
152
|
agent: specKey,
|
|
143
153
|
phase: 'phase1',
|
|
144
154
|
model: 'haiku',
|
|
145
155
|
status: 'running',
|
|
146
156
|
phase_delta_tokens: 0,
|
|
147
|
-
started_at:
|
|
157
|
+
started_at: '__TS__',
|
|
148
158
|
completed_at: null,
|
|
149
159
|
})
|
|
150
160
|
const beforeSpec = budget.spent()
|
|
@@ -152,7 +162,7 @@ if (discoveryResult.needs_tech_spec) {
|
|
|
152
162
|
const specTokens = budget.spent() - beforeSpec
|
|
153
163
|
await updateLedgerEntry(feature_dir, prefix, specKey, {
|
|
154
164
|
status: 'done',
|
|
155
|
-
completed_at:
|
|
165
|
+
completed_at: '__TS__',
|
|
156
166
|
phase_delta_tokens: specTokens,
|
|
157
167
|
})
|
|
158
168
|
tokenLedger.push({ agent: 'generate-tech-spec', model: 'haiku', phase_delta_tokens: specTokens })
|
|
@@ -171,14 +181,13 @@ const MAX_CYCLES = 3
|
|
|
171
181
|
for (let cycle = 1; cycle <= MAX_CYCLES; cycle++) {
|
|
172
182
|
log(`validate-feature-docs cycle ${cycle}`)
|
|
173
183
|
const valKey = `validate-feature-docs:phase1:cycle${cycle}`
|
|
174
|
-
const valStartedAt = new Date().toISOString()
|
|
175
184
|
await appendLedgerEntry(feature_dir, prefix, {
|
|
176
185
|
agent: valKey,
|
|
177
186
|
phase: 'phase1',
|
|
178
187
|
model: 'haiku',
|
|
179
188
|
status: 'running',
|
|
180
189
|
phase_delta_tokens: 0,
|
|
181
|
-
started_at:
|
|
190
|
+
started_at: '__TS__',
|
|
182
191
|
completed_at: null,
|
|
183
192
|
})
|
|
184
193
|
const beforeVal = budget.spent()
|
|
@@ -186,7 +195,7 @@ for (let cycle = 1; cycle <= MAX_CYCLES; cycle++) {
|
|
|
186
195
|
const valTokens = budget.spent() - beforeVal
|
|
187
196
|
await updateLedgerEntry(feature_dir, prefix, valKey, {
|
|
188
197
|
status: 'done',
|
|
189
|
-
completed_at:
|
|
198
|
+
completed_at: '__TS__',
|
|
190
199
|
phase_delta_tokens: valTokens,
|
|
191
200
|
})
|
|
192
201
|
tokenLedger.push({ agent: `validate-feature-docs (cycle ${cycle})`, model: 'haiku', phase_delta_tokens: valTokens })
|
|
@@ -13,18 +13,30 @@ export const meta = {
|
|
|
13
13
|
|
|
14
14
|
async function appendLedgerEntry(featureDir, prefix, entry) {
|
|
15
15
|
const ledgerPath = `${featureDir}/${prefix}-token-ledger.json`
|
|
16
|
-
|
|
16
|
+
// started_at is generated via Bash inside the agent to get a real UTC timestamp with time
|
|
17
|
+
const entryWithoutTs = JSON.stringify({ ...entry, started_at: '__TS__', completed_at: null })
|
|
17
18
|
await agent(
|
|
18
|
-
`Append a JSON object to the ledger array at: ${ledgerPath}\n\
|
|
19
|
+
`Append a JSON object to the ledger array at: ${ledgerPath}\n\n` +
|
|
20
|
+
`1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
|
|
21
|
+
`2. Read the file. If it does not exist or cannot be parsed as a JSON array, start with [].\n` +
|
|
22
|
+
`3. Push this object onto the array, replacing "__TS__" in started_at with NOW: ${entryWithoutTs}\n` +
|
|
23
|
+
`4. Write the full array back (JSON, 2-space indent). Return no output.`,
|
|
19
24
|
{ label: 'append-ledger', phase: 'Work Breakdown', model: 'haiku' }
|
|
20
25
|
)
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
async function updateLedgerEntry(featureDir, prefix, agentKey, updates) {
|
|
24
29
|
const ledgerPath = `${featureDir}/${prefix}-token-ledger.json`
|
|
25
|
-
|
|
30
|
+
// completed_at is generated via Bash inside the agent to get a real UTC timestamp with time
|
|
31
|
+
const updatesWithoutTs = JSON.stringify({ ...updates, completed_at: '__TS__' })
|
|
26
32
|
await agent(
|
|
27
|
-
`Update an entry in the ledger array at: ${ledgerPath}\n\
|
|
33
|
+
`Update an entry in the ledger array at: ${ledgerPath}\n\n` +
|
|
34
|
+
`1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
|
|
35
|
+
`2. Read the file. If it does not exist or cannot be parsed as a JSON array, do nothing.\n` +
|
|
36
|
+
`3. Search from the end for the last entry where agent === "${agentKey}".\n` +
|
|
37
|
+
`4. If found, merge these fields into that entry, replacing "__TS__" in completed_at with NOW: ${updatesWithoutTs}\n` +
|
|
38
|
+
`5. Write the full array back (JSON, 2-space indent). Return no output.\n` +
|
|
39
|
+
`6. If not found, do nothing.`,
|
|
28
40
|
{ label: 'update-ledger', phase: 'Work Breakdown', model: 'haiku' }
|
|
29
41
|
)
|
|
30
42
|
}
|
|
@@ -45,14 +57,13 @@ phase('Work Breakdown')
|
|
|
45
57
|
const tokenLedger = []
|
|
46
58
|
|
|
47
59
|
log(`Running generate-work-breakdown for ${featurePath}`)
|
|
48
|
-
const wbStartedAt = new Date().toISOString()
|
|
49
60
|
await appendLedgerEntry(featureDir, prefix, {
|
|
50
61
|
agent: 'generate-work-breakdown:phase2',
|
|
51
62
|
phase: 'phase2',
|
|
52
63
|
model: 'haiku',
|
|
53
64
|
status: 'running',
|
|
54
65
|
phase_delta_tokens: 0,
|
|
55
|
-
started_at:
|
|
66
|
+
started_at: '__TS__',
|
|
56
67
|
completed_at: null,
|
|
57
68
|
})
|
|
58
69
|
const beforeWB = budget.spent()
|
|
@@ -64,7 +75,7 @@ await agent(featurePath, {
|
|
|
64
75
|
const wbTokens = budget.spent() - beforeWB
|
|
65
76
|
await updateLedgerEntry(featureDir, prefix, 'generate-work-breakdown:phase2', {
|
|
66
77
|
status: 'done',
|
|
67
|
-
completed_at:
|
|
78
|
+
completed_at: '__TS__',
|
|
68
79
|
phase_delta_tokens: wbTokens,
|
|
69
80
|
})
|
|
70
81
|
tokenLedger.push({ agent: 'generate-work-breakdown', model: 'haiku', phase_delta_tokens: wbTokens })
|
|
@@ -11,6 +11,40 @@ export const meta = {
|
|
|
11
11
|
],
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
// ── Ledger helper functions ───────────────────────────────────────────────────
|
|
15
|
+
// Route all ledger I/O through agent() — fs and require() are not available in
|
|
16
|
+
// the Workflow runtime. Mirrors the pattern used by pm-phase2.js.
|
|
17
|
+
|
|
18
|
+
async function appendLedgerEntry(featureDir, prefix, entry) {
|
|
19
|
+
const ledgerPath = `${featureDir}/${prefix}-token-ledger.json`
|
|
20
|
+
const entryWithoutTs = JSON.stringify({ ...entry, started_at: '__TS__', completed_at: null })
|
|
21
|
+
await agent(
|
|
22
|
+
`Append a JSON object to the ledger array at: ${ledgerPath}\n\n` +
|
|
23
|
+
`1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
|
|
24
|
+
`2. Read the file. If it does not exist or cannot be parsed as a JSON array, start with [].\n` +
|
|
25
|
+
`3. Push this object onto the array, replacing "__TS__" in started_at with NOW: ${entryWithoutTs}\n` +
|
|
26
|
+
`4. Write the full array back (JSON, 2-space indent). Return no output.`,
|
|
27
|
+
{ label: 'append-ledger', phase: 'Implementation', model: 'haiku' }
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function updateLedgerEntry(featureDir, prefix, agentKey, updates) {
|
|
32
|
+
const ledgerPath = `${featureDir}/${prefix}-token-ledger.json`
|
|
33
|
+
const updatesWithoutTs = JSON.stringify({ ...updates, completed_at: '__TS__' })
|
|
34
|
+
await agent(
|
|
35
|
+
`Update an entry in the ledger array at: ${ledgerPath}\n\n` +
|
|
36
|
+
`1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
|
|
37
|
+
`2. Read the file. If it does not exist or cannot be parsed as a JSON array, do nothing.\n` +
|
|
38
|
+
`3. Search from the end for the last entry where agent === "${agentKey}".\n` +
|
|
39
|
+
`4. If found, merge these fields into that entry, replacing "__TS__" in completed_at with NOW: ${updatesWithoutTs}\n` +
|
|
40
|
+
`5. Write the full array back (JSON, 2-space indent). Return no output.\n` +
|
|
41
|
+
`6. If not found, do nothing.`,
|
|
42
|
+
{ label: 'update-ledger', phase: 'Implementation', model: 'haiku' }
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ── Parse args ────────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
14
48
|
// args: "<path-to-feature.md> --branch feature/FTR-NNN-slug"
|
|
15
49
|
const argStr = typeof args === 'string' ? args : ''
|
|
16
50
|
const featurePath = argStr.trim().split(/\s+/)[0]
|
|
@@ -31,10 +65,13 @@ const csvPath = `${featureDir}/${prefix}-Work-Breakdown.csv`
|
|
|
31
65
|
|
|
32
66
|
log(`Reading CSV: ${csvPath}`)
|
|
33
67
|
|
|
68
|
+
await appendLedgerEntry(featureDir, prefix, { agent: 'read-wb-csv:phase3', phase: 'phase3', model: 'haiku', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
|
|
69
|
+
const beforeCsv = budget.spent()
|
|
34
70
|
const csvContent = await agent(
|
|
35
71
|
`Read the file at path: ${csvPath}\nReturn ONLY the raw file contents, nothing else. No explanation, no formatting, no JSON wrapping — just the raw text of the file.`,
|
|
36
72
|
{ label: 'read-wb-csv', phase: 'Parse', model: 'haiku' }
|
|
37
73
|
)
|
|
74
|
+
await updateLedgerEntry(featureDir, prefix, 'read-wb-csv:phase3', { status: 'done', completed_at: '__TS__', phase_delta_tokens: budget.spent() - beforeCsv })
|
|
38
75
|
|
|
39
76
|
// Parse CSV into structured phases — pure JS, no AI
|
|
40
77
|
const rows = csvContent
|
|
@@ -148,6 +185,8 @@ const executePhase = async (implPhase) => {
|
|
|
148
185
|
|
|
149
186
|
// Step 1 — impl groups in parallel (INFRA / BE / FE)
|
|
150
187
|
if (implPhase.impl_groups.length > 0) {
|
|
188
|
+
const implKey = `${implPhase.impl_groups.map(g => g.agent_type).join('+')}:${implPhase.phase_id}${reworkCycle > 0 ? ':rework' + reworkCycle : ''}`
|
|
189
|
+
await appendLedgerEntry(featureDir, prefix, { agent: implKey, phase: 'phase3', model: 'sonnet', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
|
|
151
190
|
const beforeImpl = budget.spent()
|
|
152
191
|
await parallel(implPhase.impl_groups.map(group => () =>
|
|
153
192
|
agent(
|
|
@@ -160,16 +199,15 @@ const executePhase = async (implPhase) => {
|
|
|
160
199
|
)
|
|
161
200
|
))
|
|
162
201
|
const implTokens = budget.spent() - beforeImpl
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
model: 'sonnet',
|
|
166
|
-
phase_delta_tokens: implTokens,
|
|
167
|
-
})
|
|
202
|
+
await updateLedgerEntry(featureDir, prefix, implKey, { status: 'done', completed_at: '__TS__', phase_delta_tokens: implTokens })
|
|
203
|
+
tokenLedger.push({ agent: implKey, model: 'sonnet', phase_delta_tokens: implTokens })
|
|
168
204
|
log(`Phase ${implPhase.phase_id} impl groups done — ${implTokens} tokens`)
|
|
169
205
|
}
|
|
170
206
|
|
|
171
207
|
// Step 2 — test groups in parallel (TEST) — after impl groups
|
|
172
208
|
if (implPhase.test_groups.length > 0) {
|
|
209
|
+
const testKey = `developer-testing:${implPhase.phase_id}${reworkCycle > 0 ? ':rework' + reworkCycle : ''}`
|
|
210
|
+
await appendLedgerEntry(featureDir, prefix, { agent: testKey, phase: 'phase3', model: 'sonnet', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
|
|
173
211
|
const beforeTest = budget.spent()
|
|
174
212
|
await parallel(implPhase.test_groups.map(group => () =>
|
|
175
213
|
agent(
|
|
@@ -182,15 +220,14 @@ const executePhase = async (implPhase) => {
|
|
|
182
220
|
)
|
|
183
221
|
))
|
|
184
222
|
const testTokens = budget.spent() - beforeTest
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
model: 'sonnet',
|
|
188
|
-
phase_delta_tokens: testTokens,
|
|
189
|
-
})
|
|
223
|
+
await updateLedgerEntry(featureDir, prefix, testKey, { status: 'done', completed_at: '__TS__', phase_delta_tokens: testTokens })
|
|
224
|
+
tokenLedger.push({ agent: testKey, model: 'sonnet', phase_delta_tokens: testTokens })
|
|
190
225
|
log(`Phase ${implPhase.phase_id} test groups done — ${testTokens} tokens`)
|
|
191
226
|
}
|
|
192
227
|
|
|
193
228
|
// Step 3 — review-solution
|
|
229
|
+
const reviewKey = `review-solution:${implPhase.phase_id}`
|
|
230
|
+
await appendLedgerEntry(featureDir, prefix, { agent: reviewKey, phase: 'phase3', model: 'sonnet', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
|
|
194
231
|
const beforeReview = budget.spent()
|
|
195
232
|
const review = await agent(
|
|
196
233
|
`${featurePath} --scope ${implPhase.phase_id}`,
|
|
@@ -202,8 +239,9 @@ const executePhase = async (implPhase) => {
|
|
|
202
239
|
}
|
|
203
240
|
)
|
|
204
241
|
const reviewTokens = budget.spent() - beforeReview
|
|
242
|
+
await updateLedgerEntry(featureDir, prefix, reviewKey, { status: 'done', completed_at: '__TS__', phase_delta_tokens: reviewTokens })
|
|
205
243
|
tokenLedger.push({
|
|
206
|
-
agent:
|
|
244
|
+
agent: reviewKey,
|
|
207
245
|
model: 'sonnet',
|
|
208
246
|
phase_delta_tokens: reviewTokens,
|
|
209
247
|
})
|
|
@@ -238,16 +276,6 @@ const executePhase = async (implPhase) => {
|
|
|
238
276
|
}
|
|
239
277
|
|
|
240
278
|
phasesDone++
|
|
241
|
-
|
|
242
|
-
// Persist the cumulative token ledger to disk after every completed phase.
|
|
243
|
-
// budget.spent() is an in-memory counter — if the workflow is stopped (manual
|
|
244
|
-
// stop, crash, timeout) before the Actuals phase, all token data would be lost.
|
|
245
|
-
// Writing the full ledger here means the file always reflects real consumption
|
|
246
|
-
// up to the last completed phase, recoverable manually or by a resumed run.
|
|
247
|
-
await agent(
|
|
248
|
-
`Write the following JSON to ${featureDir}/${prefix}-token-ledger.json (overwrite the file if it exists):\n${JSON.stringify(tokenLedger, null, 2)}`,
|
|
249
|
-
{ label: `persist-ledger:${implPhase.phase_id}`, phase: 'Implementation', model: 'haiku' }
|
|
250
|
-
)
|
|
251
279
|
}
|
|
252
280
|
|
|
253
281
|
// Execute waves: phases in the same wave run in parallel, waves are sequential
|
|
@@ -285,6 +313,7 @@ log(`Implementation complete — ${phasesDone} phases, ${usPassed.length} US pas
|
|
|
285
313
|
// contention down to a single centralized run with one consolidated failure report.
|
|
286
314
|
phase('Test')
|
|
287
315
|
|
|
316
|
+
await appendLedgerEntry(featureDir, prefix, { agent: 'final-test-run', phase: 'phase3', model: 'haiku', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
|
|
288
317
|
const beforeTest = budget.spent()
|
|
289
318
|
await agent(
|
|
290
319
|
`Run the full test suite in the repository root and report the result.
|
|
@@ -297,6 +326,7 @@ Do NOT fix failing tests — only report them.`,
|
|
|
297
326
|
{ label: 'final-test-run', phase: 'Test', model: 'haiku' }
|
|
298
327
|
)
|
|
299
328
|
const testTokens = budget.spent() - beforeTest
|
|
329
|
+
await updateLedgerEntry(featureDir, prefix, 'final-test-run', { status: 'done', completed_at: '__TS__', phase_delta_tokens: testTokens })
|
|
300
330
|
tokenLedger.push({ agent: 'final-test-run', model: 'haiku', phase_delta_tokens: testTokens })
|
|
301
331
|
log(`Test run complete — ${testTokens} tokens`)
|
|
302
332
|
|
|
@@ -317,6 +347,7 @@ let issuesDeferred = 0
|
|
|
317
347
|
|
|
318
348
|
if (issuesOpen > 0 && issuesPath) {
|
|
319
349
|
log(`Remediation: ${issuesOpen} OPEN issues`)
|
|
350
|
+
await appendLedgerEntry(featureDir, prefix, { agent: 'remediation', phase: 'phase3', model: 'sonnet', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
|
|
320
351
|
const beforeRem = budget.spent()
|
|
321
352
|
|
|
322
353
|
const remResult = await agent(
|
|
@@ -339,6 +370,7 @@ Update the Issues Register Status column in place for each resolved/deferred iss
|
|
|
339
370
|
)
|
|
340
371
|
|
|
341
372
|
const remTokens = budget.spent() - beforeRem
|
|
373
|
+
await updateLedgerEntry(featureDir, prefix, 'remediation', { status: 'done', completed_at: '__TS__', phase_delta_tokens: remTokens })
|
|
342
374
|
tokenLedger.push({ agent: 'remediation', model: 'sonnet', phase_delta_tokens: remTokens })
|
|
343
375
|
issuesFixed = remResult.issues_fixed || 0
|
|
344
376
|
issuesDeferred = remResult.issues_deferred || 0
|
|
@@ -359,6 +391,7 @@ const PR_SCHEMA = {
|
|
|
359
391
|
required: ['pr_url'],
|
|
360
392
|
}
|
|
361
393
|
|
|
394
|
+
await appendLedgerEntry(featureDir, prefix, { agent: 'pr-and-registry', phase: 'phase3', model: 'sonnet', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
|
|
362
395
|
const beforePR = budget.spent()
|
|
363
396
|
|
|
364
397
|
const prResult = await agent(
|
|
@@ -408,6 +441,7 @@ If the PR creation fails, return { "pr_url": "(PR creation failed)", "registry_u
|
|
|
408
441
|
)
|
|
409
442
|
|
|
410
443
|
const prTokens = budget.spent() - beforePR
|
|
444
|
+
await updateLedgerEntry(featureDir, prefix, 'pr-and-registry', { status: 'done', completed_at: '__TS__', phase_delta_tokens: prTokens })
|
|
411
445
|
tokenLedger.push({ agent: 'pr-and-registry', model: 'sonnet', phase_delta_tokens: prTokens })
|
|
412
446
|
log(`PR done: ${prResult.pr_url} — ${prTokens} tokens`)
|
|
413
447
|
|
|
@@ -494,6 +528,7 @@ const ACTUALS_SCHEMA = {
|
|
|
494
528
|
required: [],
|
|
495
529
|
}
|
|
496
530
|
|
|
531
|
+
await appendLedgerEntry(featureDir, prefix, { agent: 'write-actuals', phase: 'phase3', model: 'sonnet', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
|
|
497
532
|
const beforeActuals = budget.spent()
|
|
498
533
|
|
|
499
534
|
await agent(
|
|
@@ -587,6 +622,7 @@ Return { "token_estimate_path": "<path>", "effort_estimate_path": "<path>" }.`,
|
|
|
587
622
|
)
|
|
588
623
|
|
|
589
624
|
const actualsTokens = budget.spent() - beforeActuals
|
|
625
|
+
await updateLedgerEntry(featureDir, prefix, 'write-actuals', { status: 'done', completed_at: '__TS__', phase_delta_tokens: actualsTokens })
|
|
590
626
|
tokenLedger.push({ agent: 'write-actuals', model: 'sonnet', phase_delta_tokens: actualsTokens })
|
|
591
627
|
log(`Actuals written — ${actualsTokens} tokens`)
|
|
592
628
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
## 0.1.4 (2026-07-21)
|
|
2
|
+
## [0.9.0](https://github.com/dtlab-tech/ai-toolkit/compare/v0.8.0...v0.9.0) (2026-08-05)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* **FTR-020:** add initial documentation for Atomic Work Breakdown, Execution Ledger, and Task Checkpoints and Resume ([c5f724a](https://github.com/dtlab-tech/ai-toolkit/commit/c5f724af068e003d36ec196cdec3502fe0c47333))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
* **pm-phase3:** replace require/fs/execSync with agent()-based ledger helpers ([1166d6f](https://github.com/dtlab-tech/ai-toolkit/commit/1166d6f17e913405ea737e74236742569740cd0a))
|
|
13
|
+
* **pm-phase3:** replace require/fs/execSync with agent()-based ledger helpers ([bf4077c](https://github.com/dtlab-tech/ai-toolkit/commit/bf4077cca28920876185535aa553ec91c6ffc08b))
|
|
14
|
+
* **pm-phase3:** replace require/fs/execSync with agent()-based ledger helpers ([bb4c88d](https://github.com/dtlab-tech/ai-toolkit/commit/bb4c88df20c62db09de327975ab57e045247dc51))
|
|
15
|
+
* **workflows:** remove new Date() from all workflow call sites ([343b604](https://github.com/dtlab-tech/ai-toolkit/commit/343b604dba2d4d0b12e5590c1b5323b2a9e888f2))
|
|
16
|
+
|
|
2
17
|
## [0.8.0](https://github.com/dtlab-tech/ai-toolkit/compare/v0.7.0...v0.8.0) (2026-08-04)
|
|
3
18
|
|
|
4
19
|
|