amsd-pipeline-workflow 0.1.12 → 0.1.13

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.
Files changed (2) hide show
  1. package/driver/run.js +33 -0
  2. package/package.json +1 -1
package/driver/run.js CHANGED
@@ -404,6 +404,24 @@ function provisionCodeline(ctx, cl, wt) {
404
404
  if (fs.existsSync(ap)) { fs.mkdirSync(epam, { recursive: true }); fs.copyFileSync(ap, path.join(epam, 'anti-patterns.json')); }
405
405
  }
406
406
 
407
+ /** A story's newest review record: the lane log's review-verdict-<story>.json, else (a run from before that record) the
408
+ * newest review-feedback.json any of this lane's Writers left; null when there is none. */
409
+ function reviewRecord(ctx, lane, env, id) {
410
+ const read = (f) => { try { return JSON.parse(fs.readFileSync(f, 'utf8')); } catch { return null; } };
411
+ const v = env.LOG_DIR && read(path.join(env.LOG_DIR, `review-verdict-${id}.json`));
412
+ if (v) return { verdict: v.verdict, from: `review-verdict-${id}.json` };
413
+ const base = path.join(ctx.runDir, 'workflows', lane.cl);
414
+ const found = [];
415
+ for (const line of (fs.existsSync(base) ? fs.readdirSync(base) : [])) for (const tag of (fs.existsSync(path.join(base, line)) ? fs.readdirSync(path.join(base, line)) : [])) {
416
+ const f = path.join(base, line, tag, 'writer', id, 'checks', 'runs', 'review-feedback.json');
417
+ if (fs.existsSync(f)) found.push({ f, t: fs.statSync(f).mtimeMs });
418
+ }
419
+ if (!found.length) return null;
420
+ const newest = found.sort((a, b) => b.t - a.t)[0];
421
+ const fb = read(newest.f);
422
+ return fb ? { verdict: fb.verdict, from: path.relative(ctx.runDir, newest.f) } : null;
423
+ }
424
+
407
425
  // ── ONE PHASE OF ONE LANE: what `bash run-agent-orchestration.sh` does for it, around the two workflows ─────────────
408
426
  // Returns the phase's exit code (lib/phase-exit.sh).
409
427
  async function phaseRunner(ctx, lane, laneEnv, { prd, phase, args, tag, lineSlug }) {
@@ -425,6 +443,14 @@ async function phaseRunner(ctx, lane, laneEnv, { prd, phase, args, tag, lineSlug
425
443
  const p = JSON.parse(fs.readFileSync(prd, 'utf8'));
426
444
  const rq = (s) => ((s.status === 'failed' || s.status === 'blocked') && !(s.completed) ? { ...s, status: 'pending' } : s);
427
445
  p.stories = (p.stories || []).map(rq);
446
+ // ONCE REVIEWED, LOCKED (operator, 2026-10-20): a completed story whose newest review record is an approval is neither
447
+ // rewritten nor re-reviewed; an "escalated" stamp an EARLIER attempt left is cleared as the pipeline clears it on
448
+ // approval (lib/review-cycle.sh ~:180–195) — the stale stamp would otherwise fail Step 5 on work already approved
449
+ for (const s of p.stories) {
450
+ if (!(s.completed === true && s.reviewStatus === 'escalated')) continue;
451
+ const rec = reviewRecord(ctx, lane, env, s.id);
452
+ if (rec && rec.verdict === 'approved') { s.reviewStatus = null; log(`[driver] ${s.id}: locked — reviewed and approved (${rec.from}); its earlier escalated stamp is cleared, nothing is rewritten or re-reviewed`); }
453
+ }
428
454
  fs.writeFileSync(prd, JSON.stringify(p, null, 2));
429
455
  }
430
456
  // RAO:1112–1123 — the spec pass, unless the lane resumed with its spec done
@@ -471,6 +497,13 @@ async function phaseRunner(ctx, lane, laneEnv, { prd, phase, args, tag, lineSlug
471
497
  }
472
498
  // what Steps 3.545 and 3.55 stamp on the story, so a retry inherits the finding (driver/writer-stamps.js)
473
499
  if (st) applyGateStamps(st, res);
500
+ // THE LOCK, RECORDED where a later Writer cannot wipe it (the lane's log; its out dir is rebuilt each leg, B64): a Writer
501
+ // that ends 'done' ended on an approved review
502
+ if (res.status === 'done') {
503
+ const head = spawnSync('git', ['-C', lane.wt, 'rev-parse', 'HEAD'], { encoding: 'utf8' });
504
+ fs.mkdirSync(env.LOG_DIR, { recursive: true });
505
+ fs.writeFileSync(path.join(env.LOG_DIR, `review-verdict-${s.id}.json`), `${JSON.stringify({ verdict: 'approved', head: head.status === 0 ? head.stdout.trim() : null, at: new Date().toISOString() })}\n`);
506
+ }
474
507
  fs.writeFileSync(prd, JSON.stringify(cur, null, 2));
475
508
  // lib/prd-state.sh update_story_status (the pipeline's), on the lane's PRD
476
509
  bash({ ...env, MAIN_PRD_FILE: prd, PRD_FILE: prd }, 'update_story_status', [s.id, res.exit === 0 ? 'completed' : 'failed'], { log });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amsd-pipeline-workflow",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "The AMSD pipeline as Claude workflows. Installed by the amsd-pipeline installer: install.sh --workflows",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {