valent-pipeline 0.19.38 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valent-pipeline",
3
- "version": "0.19.38",
3
+ "version": "0.19.39",
4
4
  "description": "v3 multi-agent AI pipeline for software development lifecycle",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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 ${velocity} --backlog ${backlogPath}\` ` +
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 ${velocity} velocity)`)
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
@@ -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>,
@@ -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'}"
@@ -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);
@@ -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',