valent-pipeline 0.19.44 → 0.19.46

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.44",
3
+ "version": "0.19.46",
4
4
  "description": "v3 multi-agent AI pipeline for software development lifecycle",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,7 @@ import { dirname, join, isAbsolute } from 'path';
16
16
  import { randomBytes } from 'crypto';
17
17
  import { parse as parseYaml } from 'yaml';
18
18
  import {
19
- runEvidence, pinGate, formatRunTrailer, formatRunDigest, resolveRoot, evidenceDir, latestRun, resolveRunForAssert, detectLabelMultiplex, latestPins,
19
+ runEvidence, pinGate, formatRunTrailer, formatRunDigest, resolveRoot, evidenceDir, latestRun, resolveRunForAssert, requiredCaseIdsForRun, detectLabelMultiplex, latestPins,
20
20
  verifyIntegrity, assertRules, assertRedRules, pinSourceDelta, snapshotFiles, buildProof, utcCompact,
21
21
  latestAssert, collectFailingCases, authenticateBaseline, verifyShipEvidence, sha256Hex,
22
22
  validStoryId, recordStageResults,
@@ -180,15 +180,18 @@ export async function evidenceAssertCmd(options) {
180
180
  process.exit(2);
181
181
  }
182
182
 
183
- // Required acceptance case ids, read early so the run resolver can bind to the suite that actually
184
- // carries them when a project multiplexes suites under one label (HARNESS-GAPS 2026-06-18 Gap 2).
183
+ // Required case ids, read early so the run resolver can bind to the suite that actually carries them
184
+ // when a project multiplexes suites under one label (HARNESS-GAPS 2026-06-18 Gap 2). Prefers
185
+ // acceptance-tagged cases (normal stories) but falls back to all required id'd cases for bug-fix
186
+ // manifests (regression cases carry no `kind`) — else requiredCaseIds is empty and the resolver
187
+ // binds to the newest full-suite run, false-rejecting a clean bug fix on the count check.
185
188
  // The full manifest is re-read below for the count/skips rules.
186
189
  let requiredCaseIds = [];
187
190
  try {
188
191
  const mp = options.manifest ? abs(options.manifest) : join(root, 'stories', story, 'output', 'qa-test-spec.manifest.json');
189
192
  if (existsSync(mp)) {
190
193
  const m = JSON.parse(readFileSync(mp, 'utf-8'));
191
- if (Array.isArray(m?.cases)) requiredCaseIds = m.cases.filter((c) => c?.kind === 'acceptance' && c?.required !== false && c?.id).map((c) => c.id);
194
+ requiredCaseIds = requiredCaseIdsForRun(m?.cases);
192
195
  }
193
196
  } catch { requiredCaseIds = []; }
194
197
 
@@ -2,7 +2,7 @@ import { fileURLToPath } from 'url';
2
2
  import { dirname, join, resolve } from 'path';
3
3
  import { existsSync, readFileSync, rmSync } from 'fs';
4
4
  import { defaults } from '../lib/config-schema.js';
5
- import { copyDir, writeFileSafe, appendToGitignore, fileExists } from '../lib/file-ops.js';
5
+ import { copyDir, writeFileSafe, appendToGitignore, writeFrameworkGitignore, fileExists } from '../lib/file-ops.js';
6
6
 
7
7
  const __filename = fileURLToPath(import.meta.url);
8
8
  const __dirname = dirname(__filename);
@@ -225,12 +225,16 @@ export async function init(options = {}) {
225
225
  writeFileSafe(claudeSettingsPath, JSON.stringify(claudeSettings, null, 2) + '\n');
226
226
  console.log(` Configured .claude/settings.json (agent teams; pre-approved MCP: ${[...approved].join(', ') || 'none'})`);
227
227
 
228
- // 9. Update .gitignore
228
+ // 9. Update .gitignore. The vendored framework is an untracked INSTALL (see writeFrameworkGitignore):
229
+ // a `.valent-pipeline/.gitignore` keeps the framework code out of git so `upgrade` applies repo-wide
230
+ // and a reused story branch can never run a stale committed framework (M-52 Finding 1). The
231
+ // root-.gitignore lines below are belt-and-suspenders for the runtime artifacts.
232
+ writeFrameworkGitignore(projectRoot);
229
233
  appendToGitignore(projectRoot, '.valent-pipeline/pipeline.db');
230
234
  appendToGitignore(projectRoot, '.valent-pipeline/pipeline.db-*');
231
235
  if (usesChroma) appendToGitignore(projectRoot, '.valent-pipeline/chromadb-data/');
232
236
  appendToGitignore(projectRoot, '.valent-pipeline/node_modules/');
233
- console.log(' Updated .gitignore');
237
+ console.log(' Updated .gitignore (framework vendored as an untracked install)');
234
238
 
235
239
  // 9. Write version file (pkg was read in step 7)
236
240
  writeFileSafe(join(projectRoot, '.valent-pipeline', '.valent-version'), pkg.version);
@@ -1,7 +1,8 @@
1
1
  import { fileURLToPath } from 'url';
2
2
  import { dirname, join, resolve } from 'path';
3
3
  import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, copyFileSync, rmSync } from 'fs';
4
- import { copyDir, writeFileSafe, fileExists, readFile } from '../lib/file-ops.js';
4
+ import { execSync } from 'child_process';
5
+ import { copyDir, writeFileSafe, writeFrameworkGitignore, fileExists, readFile } from '../lib/file-ops.js';
5
6
  import { obsoletePaths } from '../lib/obsolete-manifest.js';
6
7
 
7
8
  const __filename = fileURLToPath(import.meta.url);
@@ -215,6 +216,26 @@ export async function upgrade(options = {}) {
215
216
  writeFileSafe(versionFile, packageVersion);
216
217
  console.log(`Updated .valent-pipeline/.valent-version to ${packageVersion}`);
217
218
 
219
+ // Vendor the framework as an untracked INSTALL (M-52 Finding 1): write .valent-pipeline/.gitignore so
220
+ // the framework lives only in the working tree — `upgrade` then applies repo-wide and a reused story
221
+ // branch can never run a stale committed framework. If the framework is still git-TRACKED here, print
222
+ // the one-time untrack transition (we never touch the user's git index ourselves).
223
+ writeFrameworkGitignore(projectRoot);
224
+ let frameworkTracked = false;
225
+ try {
226
+ execSync('git ls-files --error-unmatch .valent-pipeline/.valent-version', { cwd: projectRoot, stdio: 'ignore' });
227
+ frameworkTracked = true;
228
+ } catch { /* not a git repo, or already untracked — no transition needed */ }
229
+ if (frameworkTracked) {
230
+ console.log('\n⚠ The vendored framework is still git-TRACKED, so a reused/old story branch will run its');
231
+ console.log(' STALE committed framework (not this upgrade). To finish the install→untracked transition');
232
+ console.log(' ONCE, at a clean boundary, run from the repo root:');
233
+ console.log(' git rm -r --cached --quiet .valent-pipeline && git add .valent-pipeline \\');
234
+ console.log(' && git commit -m "chore: untrack vendored framework — install not source"');
235
+ console.log(' (git add re-adds ONLY pipeline-config.yaml + knowledge/ + .gitignore per the new ignore;');
236
+ console.log(' old branches then need a fresh recut off the transitioned target to pick up the install.)');
237
+ }
238
+
218
239
  console.log('\nUpgrade complete.');
219
240
  }
220
241
 
Binary file
@@ -43,6 +43,34 @@ export function appendToGitignore(projectRoot, line) {
43
43
  }
44
44
  }
45
45
 
46
+ // The vendored framework is an INSTALL (re-vendored by `valent-pipeline upgrade`), not source. Keeping
47
+ // it git-tracked meant a REUSED story branch ran whatever framework version it was last committed at
48
+ // (a bug-fix re-run silently executed a stale framework — HARNESS-GAPS 2026-06-19 M-52 Finding 1).
49
+ // This `.valent-pipeline/.gitignore` makes it untracked, so the install lives only in the working tree:
50
+ // gitignored files persist across ALL branch checkouts, one `upgrade` applies repo-wide, and no branch
51
+ // can revert it. `/*` is anchored (direct children only — future-proof to new framework dirs); only
52
+ // project STATE is re-included. KEEP IN SYNC with upgrade.js PROTECTED_FILES.
53
+ export const FRAMEWORK_GITIGNORE = `# valent-pipeline vendored framework — an INSTALL (re-vendored by \`valent-pipeline upgrade\`), not source.
54
+ # Kept UNTRACKED so an upgrade applies repo-wide and a reused story branch never runs a stale committed
55
+ # framework. Only project STATE stays tracked (pipeline-config.yaml, knowledge/).
56
+ /*
57
+ !/.gitignore
58
+ !/pipeline-config.yaml
59
+ !/knowledge/
60
+ `;
61
+
62
+ /**
63
+ * Write `.valent-pipeline/.gitignore` so the vendored framework is an untracked install. Idempotent —
64
+ * rewrites to the canonical content. Returns true if it created or changed the file.
65
+ */
66
+ export function writeFrameworkGitignore(projectRoot) {
67
+ const p = join(projectRoot, '.valent-pipeline', '.gitignore');
68
+ const existing = existsSync(p) ? readFileSync(p, 'utf-8') : null;
69
+ if (existing === FRAMEWORK_GITIGNORE) return false;
70
+ writeFileSync(p, FRAMEWORK_GITIGNORE, 'utf-8');
71
+ return true;
72
+ }
73
+
46
74
  /**
47
75
  * Check if a file exists.
48
76
  */