valent-pipeline 0.19.47 → 0.19.49

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.47",
3
+ "version": "0.19.49",
4
4
  "description": "v3 multi-agent AI pipeline for software development lifecycle",
5
5
  "type": "module",
6
6
  "bin": {
@@ -524,6 +524,10 @@ export function startStory({ root, story, targetBranch = '', prefix = 'story/',
524
524
  let refreshedFromTarget = false;
525
525
  let refreshConflict = false;
526
526
  if (basedOnStory && branchExists(root, target) && !isMergedInto(root, target, branch)) {
527
+ // Capture whether the vendored install is present BEFORE the sync, so the hazard guard below fires
528
+ // ONLY when the merge actually REMOVED it — not when a repo simply never had a materialized install
529
+ // (e.g. a bare test fixture), which must refresh normally.
530
+ const hadFrameworkInstall = existsSync(join(root, '.valent-pipeline', 'bin', 'cli.js'));
527
531
  try {
528
532
  git(root, ['merge', '--no-ff', '-m',
529
533
  buildCommitMessage({ storyId: story, phase: 'sync', summary: `sync ${target} into ${branch} (inherit shared state from a behind base)` }), target]);
@@ -532,6 +536,24 @@ export function startStory({ root, story, targetBranch = '', prefix = 'story/',
532
536
  if (mergeInProgress(root)) git(root, ['merge', '--abort']);
533
537
  refreshConflict = true;
534
538
  }
539
+ // M-52 transition hazard: when `target` UNTRACKS the vendored framework (the post-untrack install
540
+ // model) while THIS branch still TRACKS it (a pre-untrack branch), the merge above applied target's
541
+ // index-deletion to the WORKING TREE and removed the on-disk install — `.valent-pipeline/bin/cli.js`
542
+ // is gone, so the next `node .valent-pipeline/bin/cli.js` call would fail mid-sprint. Such a branch
543
+ // cannot be refreshed in place (keeping the merge loses the install; reverting it restores a STALE
544
+ // framework — the very thing untracking fixed), so undo the sync and surface it like a conflict: the
545
+ // caller must recut the branch FRESH off the untracked target. (The worktree create-path self-heals
546
+ // via setupWorktree's re-materialize; this serial path has no such restore.)
547
+ if (refreshedFromTarget && hadFrameworkInstall && !existsSync(join(root, '.valent-pipeline', 'bin', 'cli.js'))) {
548
+ git(root, ['reset', '--hard', 'ORIG_HEAD']);
549
+ refreshedFromTarget = false;
550
+ refreshConflict = true;
551
+ console.error(
552
+ `git-flow: sync of ${target} into ${branch} would DELETE the vendored framework install ` +
553
+ `(${target} untracks .valent-pipeline/ but ${branch} still tracks it — M-52 untrack transition). ` +
554
+ `Skipped the sync; ${branch} left at its base — recut it FRESH off ${target} instead of --from-story.`,
555
+ );
556
+ }
535
557
  }
536
558
  writeFlowRecord(root, story, {
537
559
  schema: 1,
@@ -560,7 +582,14 @@ export function storyStatus({ root, story, targetBranch = '', prefix = 'story/'
560
582
  const branch = storyBranchName(prefix, story);
561
583
  const exists = branchExists(root, branch);
562
584
  const record = readFlowRecord(root, story);
563
- const target = targetBranch || record?.target || currentBranch(root);
585
+ // A `--from-story` item (an inline bug fix) ships `--into` its SOURCE story branch (recorded as
586
+ // basedOnStory), NOT the sprint target — so "did it land" is ancestry against basedOnStory. The sprint
587
+ // `--target` that the post-ship verify passes globally (gitFlags `--target`) is the wrong base for such
588
+ // an item: it reads a bug correctly merged into its source — but whose source hasn't reached the sprint
589
+ // target yet — as merged:false → a FALSE git-ship-failed that stranded the parent's revalidation
590
+ // (M-60, 2.17-BUG-002). basedOnStory is therefore AUTHORITATIVE when present (it overrides even an
591
+ // explicit --target); an ordinary story (basedOnStory null) still honors --target then the record.
592
+ const target = record?.basedOnStory || targetBranch || record?.target || currentBranch(root);
564
593
  // A missing branch is UNKNOWN (merged:null), never a verdict: the flow retains branches on
565
594
  // both ship and reject, so absence means the flow never ran here (or someone pruned by hand) —
566
595
  // the caller must treat the state as unverified rather than as landed.