valent-pipeline 0.19.29 → 0.19.31

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.29",
3
+ "version": "0.19.31",
4
4
  "description": "v3 multi-agent AI pipeline for software development lifecycle",
5
5
  "type": "module",
6
6
  "bin": {
@@ -96,7 +96,26 @@ export function crosscheckVerdict({ reported = null, bugsBlock = null, assertVer
96
96
  // but do not block; they are counted separately so they stay visible).
97
97
  if (bugsBlock && Array.isArray(bugsBlock.bugs)) {
98
98
  const isPre = (b) => b.preExisting === true || b.category === 'pre-existing';
99
- const open = bugsBlock.bugs.filter((b) => OPEN_STATUSES.has(b.status) && ['P1', 'P2', 'P3'].includes(b.priority));
99
+ // JUDGE severity-demotion auto-flow (HARNESS-GAPS 2026-06-18 Gap 1b; Matt 2026-06-18). JUDGE is the
100
+ // adjudication authority and may re-grade a bug (e.g. a test-only isolation bug P3->P4). When JUDGE
101
+ // EXPLICITLY carries a bug in its verdict at a LESS-blocking priority than bugs.md filed, the recount
102
+ // honors the adjudicated priority — the demotion is an explicit, audited decision (recorded in the
103
+ // verdict, the verdict-crosscheck.json, and the backlog carry). Safety is unchanged: only a CARRIED
104
+ // bug is re-graded, and only DOWNWARD; a bug JUDGE does not carry keeps its filed priority, so a
105
+ // SILENTLY-dropped open P1-P3 (the integrity breach this guards) still mismatches. A promotion is the
106
+ // safe over-report direction and is left as filed (the recount already tolerates JUDGE over-reporting).
107
+ const PRI_RANK = { P1: 1, P2: 2, P3: 3, P4: 4 };
108
+ const adjudicated = new Map(
109
+ (reported && Array.isArray(reported.bugs) ? reported.bugs : [])
110
+ .filter((b) => b && b.id && b.priority).map((b) => [b.id, b.priority]),
111
+ );
112
+ const effPriority = (b) => {
113
+ const adj = adjudicated.get(b.id);
114
+ return adj && (PRI_RANK[adj] ?? 99) > (PRI_RANK[b.priority] ?? 99) ? adj : b.priority;
115
+ };
116
+ const demotions = bugsBlock.bugs.filter((b) => effPriority(b) !== b.priority).map((b) => ({ id: b.id, from: b.priority, to: effPriority(b) }));
117
+ if (demotions.length) recounted.adjudicatedDemotions = demotions;
118
+ const open = bugsBlock.bugs.filter((b) => OPEN_STATUSES.has(b.status) && ['P1', 'P2', 'P3'].includes(effPriority(b)));
100
119
  recounted.preExistingOpen = open.filter(isPre).length;
101
120
  compare('openP1toP3', open.filter((b) => !isPre(b)).length);
102
121
 
Binary file
package/src/lib/sprint.js CHANGED
@@ -154,14 +154,21 @@ export function packSprint(stories, velocity, opts = {}) {
154
154
  const groomed = stories.filter((s) => (!scope || scope.has(s.id)) && isExecReady(s) && bugsResolvable(s));
155
155
  const byId = new Map(groomed.map((s) => [s.id, s]));
156
156
 
157
- // Out-of-batch prerequisite check (review 2026-06-10 #20): a depends_on that is not packable
158
- // this sprint clears only if it already landed (or is absent from the file — pre-backlog work);
159
- // a pending/stranded/blocked prerequisite withholds the dependent.
160
- const outOfBatchDepsSatisfied = (story) => depsOf(story).every((depId) => {
161
- if (byId.has(depId)) return true; // packable this sprint sequenced before the story below
162
- const known = byIdAll.get(depId);
163
- return !known || satisfied.has(known.status);
164
- });
157
+ // Out-of-batch prerequisite check (review 2026-06-10 #20), now TRANSITIVE (HARNESS-GAPS 2026-06-18
158
+ // Gap 3): a depends_on clears only if it already landed (or is absent — pre-backlog work), OR is
159
+ // packable this sprint AND its OWN dependency chain likewise clears. Checking only DIRECT deps let
160
+ // an in-sprint dep whose prerequisite is unshipped slip in as a velocity-filler, building against
161
+ // unbuilt code `resolveEligibleStories` already requires the WHOLE chain to be live; the packer
162
+ // must agree or the two disagree about what runs.
163
+ const outOfBatchDepsSatisfied = (story, seen = new Set()) => {
164
+ if (seen.has(story.id)) return true; // cycle guard — a dependency cycle is a backlog error, not the packer's to resolve
165
+ seen.add(story.id);
166
+ return depsOf(story).every((depId) => {
167
+ if (byId.has(depId)) return outOfBatchDepsSatisfied(byId.get(depId), seen); // in-sprint dep — its chain must hold too
168
+ const known = byIdAll.get(depId);
169
+ return !known || satisfied.has(known.status);
170
+ });
171
+ };
165
172
 
166
173
  const byPriority = [...groomed].sort(
167
174
  (a, b) => (a.priority ?? Infinity) - (b.priority ?? Infinity),