valent-pipeline 0.19.37 → 0.19.39
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/bin/cli.js +1 -1
- package/package.json +1 -1
- package/pipeline/orchestrators/claude-code/plan.workflow.js +19 -5
- package/pipeline/orchestrators/claude-code/sprint.workflow.js +12 -0
- package/skills/valent-run-epic-workflow/SKILL.md +1 -1
- package/skills/valent-run-project-workflow/SKILL.md +1 -1
- package/src/commands/init.js +4 -0
- package/src/commands/upgrade.js +1 -1
- package/src/lib/config-schema.js +8 -0
- package/src/lib/git-flow.js +2 -0
package/bin/cli.js
CHANGED
|
@@ -472,7 +472,7 @@ gitCmd
|
|
|
472
472
|
.command('commit-phase')
|
|
473
473
|
.description('Commit everything dirty as one typed conventional commit `{type}({story}): {summary}` with Story/Phase trailers; no-op when clean')
|
|
474
474
|
.requiredOption('--story <id>', 'Story identifier')
|
|
475
|
-
.requiredOption('--phase <phase>', 'Commit phase: spec | red | build | rework | review | evidence | ship | rollover | snapshot')
|
|
475
|
+
.requiredOption('--phase <phase>', 'Commit phase: spec | red | build | rework | review | evidence | ship | rollover | snapshot | sync | backlog')
|
|
476
476
|
.option('--gate <gate>', 'Gate name for rework commits (static | critic | green | evidence)')
|
|
477
477
|
.option('-m, --message <summary>', 'Commit summary (defaults per phase)')
|
|
478
478
|
.option('--root <dir>', 'Project root (defaults to cwd)')
|
package/package.json
CHANGED
|
@@ -30,9 +30,11 @@
|
|
|
30
30
|
* The return value is shaped to feed straight into sprint.workflow.js:
|
|
31
31
|
* { sprintId, points_planned, stories: [{ storyId, projectType, profiles }] }
|
|
32
32
|
*
|
|
33
|
-
* args: { stories: [{ storyId, projectType }], sprintId, velocity, lookaheadFactor?, groomBatchSize?, backlogPath?, maxRejectionCycles?, models?, packGate? }
|
|
33
|
+
* args: { stories: [{ storyId, projectType }], sprintId, velocity, velocityGrowthFactor?, lookaheadFactor?, groomBatchSize?, backlogPath?, maxRejectionCycles?, models?, packGate? }
|
|
34
34
|
* `velocity` is the current (seeded-SMA) velocity computed by the invoking skill; grooming runs
|
|
35
|
-
* ahead to `lookaheadFactor × velocity` (default 2) in chunks of `groomBatchSize` (default 10).
|
|
35
|
+
* ahead to `lookaheadFactor × velocity` (default 2) in chunks of `groomBatchSize` (default 10). The
|
|
36
|
+
* sprint PACKS at `velocity × velocityGrowthFactor` (default 1.1) — a deliberate stretch that pushes
|
|
37
|
+
* throughput up over time, governed by the per-story quality gates (see the packVelocity note below).
|
|
36
38
|
* `models` is the pipeline-config.yaml `models` tier->roles map, passed through by the invoking
|
|
37
39
|
* skill so per-agent model tiers stay config-driven (editable via `valent configure`). Omit it to
|
|
38
40
|
* use the baked-in default. See sprint.workflow.js for the full rationale.
|
|
@@ -199,6 +201,18 @@ const velocity = a.velocity
|
|
|
199
201
|
// workflow self-hosts when the args are absent.
|
|
200
202
|
const lookaheadFactor = typeof a.lookaheadFactor === 'number' && a.lookaheadFactor > 0 ? a.lookaheadFactor : 2
|
|
201
203
|
const groomBatchSize = Number.isInteger(a.groomBatchSize) && a.groomBatchSize > 0 ? a.groomBatchSize : 10
|
|
204
|
+
// Velocity STRETCH (2026-06-19): the planner PACKS at `velocity × velocityGrowthFactor`, not the raw
|
|
205
|
+
// trailing SMA. The SMA alone is a lagging, mean-reverting measure with a DOWNWARD ratchet — the packer
|
|
206
|
+
// caps points_shipped at velocity, so any reject/rollover/bug-only sprint pulls the SMA down and nothing
|
|
207
|
+
// ever pushes it back up (velocity can only decay). A flat growth factor applies constant upward pressure
|
|
208
|
+
// while the rigorous per-story gates stay the GOVERNOR: over-reach REJECTS → points_shipped drops → the
|
|
209
|
+
// SMA self-corrects, so throughput climbs only as fast as quality allows and settles at the
|
|
210
|
+
// quality-bounded maximum instead of decaying. Default 1.1 (+10%/sprint); config `sprint.velocity_growth_factor`,
|
|
211
|
+
// passed by the skill. Applied ONLY to the pack — grooming stays on the raw velocity (lookaheadFactor×raw
|
|
212
|
+
// = 2× already covers the 1.1× pack) and the retro's measured `velocity.sma` is left honest (measurement
|
|
213
|
+
// is not the target). round() makes the capacity a whole number (story points are integers).
|
|
214
|
+
const velocityGrowthFactor = typeof a.velocityGrowthFactor === 'number' && a.velocityGrowthFactor > 0 ? a.velocityGrowthFactor : 1.1
|
|
215
|
+
const packVelocity = Math.round(velocity * velocityGrowthFactor)
|
|
202
216
|
const backlogPath = a.backlogPath || 'pipeline-backlog.yaml'
|
|
203
217
|
const maxRejectionCycles = a.maxRejectionCycles ?? 3
|
|
204
218
|
// Pack-time freshness gate config (PUR-001 R-6.2), passed by the skill from pipeline-config.yaml
|
|
@@ -648,13 +662,13 @@ if (packGateCommands.length) {
|
|
|
648
662
|
phase('Pack')
|
|
649
663
|
// Deterministic greedy packing happens in code (src/lib/sprint.js), invoked via the CLI.
|
|
650
664
|
const pack = await agent(
|
|
651
|
-
`Run exactly: \`node .valent-pipeline/bin/cli.js sprint-pack --velocity ${
|
|
665
|
+
`Run exactly: \`node .valent-pipeline/bin/cli.js sprint-pack --velocity ${packVelocity} --backlog ${backlogPath}\` ` +
|
|
652
666
|
`in the project root and return its stdout JSON verbatim (fields: sprint_stories, buffer_story_ids, points_planned, remaining_capacity, over_budget).`,
|
|
653
667
|
{ label: 'sprint-pack', phase: 'Pack', schema: PACK_SCHEMA, model: modelFor('PACK') },
|
|
654
668
|
)
|
|
655
|
-
log(`packed ${pack.sprint_stories.length} stories (${pack.points_planned} pts); buffer: ${pack.buffer_story_ids.length}`)
|
|
669
|
+
log(`packed ${pack.sprint_stories.length} stories (${pack.points_planned} pts) at capacity ${packVelocity} (velocity ${velocity} × ${velocityGrowthFactor} stretch); buffer: ${pack.buffer_story_ids.length}`)
|
|
656
670
|
if (pack.over_budget) {
|
|
657
|
-
log(`⚠ sprint ${sprintId} is OVER BUDGET: the highest-priority story exceeds velocity ${velocity} and was planned alone — consider splitting it (${pack.points_planned} pts vs ${
|
|
671
|
+
log(`⚠ sprint ${sprintId} is OVER BUDGET: the highest-priority story exceeds the packing capacity ${packVelocity} (velocity ${velocity} × ${velocityGrowthFactor}) and was planned alone — consider splitting it (${pack.points_planned} pts vs ${packVelocity} capacity)`)
|
|
658
672
|
}
|
|
659
673
|
|
|
660
674
|
// Shaped to feed straight into sprint.workflow.js. Order by `pack.sprint_stories`, NOT by
|
|
@@ -1462,6 +1462,18 @@ const backlog = await persistBacklog(
|
|
|
1462
1462
|
integration,
|
|
1463
1463
|
)
|
|
1464
1464
|
|
|
1465
|
+
// (B) Commit the backlog dispositions persistBacklog just wrote (M-28/M-19 root). persistBacklog
|
|
1466
|
+
// runs `backlog ship|reject|requeue` (which WRITE pipeline-backlog.yaml) but nothing committed the
|
|
1467
|
+
// result — it rode the NEXT start-story snapshot, and a plan/checkout that ran first (the following
|
|
1468
|
+
// sprint) discarded the uncommitted write, silently reverting shipped stories to `groomed` (the
|
|
1469
|
+
// 2.9/2.10 status-drift). Commit it NOW, on the target, so no later checkout can drop it. commit-phase
|
|
1470
|
+
// is a no-op when the tree is clean (e.g. an all-skip resume where persist wrote nothing).
|
|
1471
|
+
if (gitFlowEnabled && !(backlog && backlog.skipped)) {
|
|
1472
|
+
await runGitStep('sprint', [
|
|
1473
|
+
`node .valent-pipeline/bin/cli.js git commit-phase --story sprint --phase backlog`,
|
|
1474
|
+
], 'persist-commit', 'Backlog')
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1465
1477
|
return {
|
|
1466
1478
|
shipped: results.every((r) => r.shipped),
|
|
1467
1479
|
stories_shipped: shippedCount,
|
|
@@ -149,7 +149,7 @@ Workflow({
|
|
|
149
149
|
...resolved.groomedBuffer,
|
|
150
150
|
],
|
|
151
151
|
sprintId: '{epic_id}-sprint-{n}', velocity: {computed seeded-SMA velocity, see above},
|
|
152
|
-
lookaheadFactor: {sprint.groom_lookahead_factor or 2}, groomBatchSize: {sprint.max_groom_batch_size or 10},
|
|
152
|
+
lookaheadFactor: {sprint.groom_lookahead_factor or 2}, groomBatchSize: {sprint.max_groom_batch_size or 10}, velocityGrowthFactor: {sprint.velocity_growth_factor or 1.1},
|
|
153
153
|
maxRejectionCycles: {quality.max_rejection_cycles or 3}, backlogPath: '{backlog_path}',
|
|
154
154
|
knowledge: { mode: <config.knowledge.mode or 'sqlite'>, curatedPath: <config.knowledge.curated_files_path or 'knowledge/curated'> },
|
|
155
155
|
models: <config.models or omit>, reasoning: <config.reasoning or omit>, packGate: <config.pack_gate or omit>,
|
|
@@ -149,7 +149,7 @@ Workflow({
|
|
|
149
149
|
...resolved.groomedBuffer,
|
|
150
150
|
],
|
|
151
151
|
sprintId: 'project-sprint-{n}', velocity: {computed seeded-SMA velocity, see above},
|
|
152
|
-
lookaheadFactor: {sprint.groom_lookahead_factor or 2}, groomBatchSize: {sprint.max_groom_batch_size or 10},
|
|
152
|
+
lookaheadFactor: {sprint.groom_lookahead_factor or 2}, groomBatchSize: {sprint.max_groom_batch_size or 10}, velocityGrowthFactor: {sprint.velocity_growth_factor or 1.1},
|
|
153
153
|
maxRejectionCycles: {quality.max_rejection_cycles or 3}, backlogPath: '{backlog_path}',
|
|
154
154
|
knowledge: { mode: <config.knowledge.mode or 'sqlite'>, curatedPath: <config.knowledge.curated_files_path or 'knowledge/curated'> },
|
|
155
155
|
models: <config.models or omit>, reasoning: <config.reasoning or omit>, packGate: <config.pack_gate or omit>,
|
package/src/commands/init.js
CHANGED
|
@@ -575,6 +575,10 @@ sprint:
|
|
|
575
575
|
groom_lookahead_factor: ${config.sprint?.groom_lookahead_factor ?? 2}
|
|
576
576
|
# Trailing window (sprints) for the velocity simple moving average, seeded with initial_velocity_points.
|
|
577
577
|
velocity_sma_window: ${config.sprint?.velocity_sma_window ?? 3}
|
|
578
|
+
# Velocity STRETCH: the planner packs at velocity_sma × this factor (not the raw SMA), so throughput
|
|
579
|
+
# climbs over time instead of decaying; the per-story quality gates govern it (over-reach rejects →
|
|
580
|
+
# the SMA self-corrects). 1.0 disables the stretch.
|
|
581
|
+
velocity_growth_factor: ${config.sprint?.velocity_growth_factor ?? 1.1}
|
|
578
582
|
|
|
579
583
|
orchestration:
|
|
580
584
|
recommended_context_window: "${config.orchestration?.recommended_context_window || '200k'}"
|
package/src/commands/upgrade.js
CHANGED
|
@@ -135,7 +135,7 @@ export async function upgrade(options = {}) {
|
|
|
135
135
|
let configContent = readFile(configPath);
|
|
136
136
|
if (!configContent.includes('sprint:')) {
|
|
137
137
|
// Insert sprint section before orchestration (or at end if orchestration not found)
|
|
138
|
-
const sprintBlock = `\nsprint:\n duration_minutes: 480\n initial_velocity_points: 60\n estimation_model: "calibrated"\n auto_plan: true\n fibonacci_scale: [1, 2, 3, 5, 8, 13, 21]\n max_groom_batch_size: 10\n groom_lookahead_factor: 2\n velocity_sma_window: 3\n`;
|
|
138
|
+
const sprintBlock = `\nsprint:\n duration_minutes: 480\n initial_velocity_points: 60\n estimation_model: "calibrated"\n auto_plan: true\n fibonacci_scale: [1, 2, 3, 5, 8, 13, 21]\n max_groom_batch_size: 10\n groom_lookahead_factor: 2\n velocity_sma_window: 3\n velocity_growth_factor: 1.1\n`;
|
|
139
139
|
const insertPoint = configContent.indexOf('\norchestration:');
|
|
140
140
|
if (insertPoint !== -1) {
|
|
141
141
|
configContent = configContent.slice(0, insertPoint) + sprintBlock + configContent.slice(insertPoint);
|
package/src/lib/config-schema.js
CHANGED
|
@@ -288,6 +288,7 @@ export function validateConfig(config) {
|
|
|
288
288
|
max_groom_batch_size: 'number',
|
|
289
289
|
groom_lookahead_factor: 'number',
|
|
290
290
|
velocity_sma_window: 'number',
|
|
291
|
+
velocity_growth_factor: 'number',
|
|
291
292
|
};
|
|
292
293
|
for (const [field, type] of Object.entries(sprintFields)) {
|
|
293
294
|
if (config.sprint[field] !== undefined && typeof config.sprint[field] !== type) {
|
|
@@ -562,6 +563,13 @@ export const defaults = {
|
|
|
562
563
|
// Trailing window (in sprints) for the velocity simple moving average. The SMA is seeded with
|
|
563
564
|
// initial_velocity_points so sprints 1..velocity_sma_window ramp smoothly before the seed ages out.
|
|
564
565
|
velocity_sma_window: 3,
|
|
566
|
+
// Velocity STRETCH multiplier. The planner PACKS at `velocity_sma × velocity_growth_factor` (not the
|
|
567
|
+
// raw SMA), applying constant upward pressure so throughput climbs over time instead of decaying — a
|
|
568
|
+
// plain SMA is lagging + mean-reverting and only ratchets DOWN (the packer caps shipped points at
|
|
569
|
+
// velocity, so any reject/rollover pulls it down and nothing pushes it back up). The per-story quality
|
|
570
|
+
// gates are the governor: over-reach rejects → shipped points drop → the SMA self-corrects, so velocity
|
|
571
|
+
// rises only as fast as quality holds and settles at the quality-bounded max. 1.0 disables the stretch.
|
|
572
|
+
velocity_growth_factor: 1.1,
|
|
565
573
|
},
|
|
566
574
|
orchestration: {
|
|
567
575
|
recommended_context_window: '200k',
|
package/src/lib/git-flow.js
CHANGED
|
@@ -53,6 +53,7 @@ export const PHASE_TYPES = {
|
|
|
53
53
|
ship: 'chore', // final sweep before merge: post-review rework + QA/judge artifacts
|
|
54
54
|
rollover: 'chore', // reject/blocked sweep so the branch holds everything for the fix/retry
|
|
55
55
|
sync: 'chore', // re-ship prep: target merged into the retained branch (sync-story)
|
|
56
|
+
backlog: 'chore', // sprint-end backlog dispositions, committed AT persist so a later plan/checkout cannot discard the uncommitted write (M-28/M-19)
|
|
56
57
|
};
|
|
57
58
|
|
|
58
59
|
const DEFAULT_SUMMARIES = {
|
|
@@ -66,6 +67,7 @@ const DEFAULT_SUMMARIES = {
|
|
|
66
67
|
rollover: 'roll over story work',
|
|
67
68
|
snapshot: 'snapshot uncommitted work before story branch',
|
|
68
69
|
sync: 'sync target into story branch before re-ship',
|
|
70
|
+
backlog: 'persist sprint backlog dispositions',
|
|
69
71
|
};
|
|
70
72
|
|
|
71
73
|
function git(cwd, args, opts = {}) {
|