amsd-pipeline-workflow 0.1.14 → 0.1.16

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.
@@ -0,0 +1,112 @@
1
+ {
2
+ "component": {
3
+ "name": "amsd-pipeline-workflow",
4
+ "package": "amsd-pipeline-workflow",
5
+ "description": "the AMSD pipeline as Claude workflows: the driver, the workflow generators, the hooks and the launch tools"
6
+ },
7
+ "scope": {
8
+ "repairDirs": [
9
+ "driver",
10
+ "generator",
11
+ "hooks",
12
+ "mcp"
13
+ ],
14
+ "testDirs": [
15
+ "test"
16
+ ],
17
+ "testFile": "^test/.*\\.test\\.js$",
18
+ "forbiddenTests": [
19
+ "(^|/)driver-[^/]*\\.test\\.js$"
20
+ ]
21
+ },
22
+ "test": {
23
+ "command": [
24
+ "{node}",
25
+ "--test",
26
+ "{files}"
27
+ ],
28
+ "envUnset": [
29
+ "NODE_TEST_CONTEXT"
30
+ ],
31
+ "greenPatterns": [
32
+ "ℹ pass [1-9]",
33
+ "ℹ fail 0",
34
+ "✔ (?!\\S+\\.js \\()"
35
+ ],
36
+ "failedPattern": "ℹ fail [1-9]"
37
+ },
38
+ "install": {
39
+ "dest": "claude-workflows",
40
+ "requires": [
41
+ "orchestrations/scripts",
42
+ "orchestrations/projects"
43
+ ],
44
+ "command": [
45
+ "{node}",
46
+ "{worktree}/bin/amsd-pipeline-workflow.js",
47
+ "install",
48
+ "--into",
49
+ "{install}"
50
+ ],
51
+ "manifest": "package.json#files",
52
+ "patchRecord": "doctor-patches.json"
53
+ },
54
+ "crash": {
55
+ "file": "crash.json"
56
+ },
57
+ "classify": {
58
+ "runtime": "node",
59
+ "refusals": [
60
+ "never works a real codeline",
61
+ "cannot resume run",
62
+ "operator's yes",
63
+ "needs the operator"
64
+ ]
65
+ },
66
+ "settings": {
67
+ "on": "EPAM_PIPELINE_DOCTOR",
68
+ "restarts": "EPAM_PIPELINE_DOCTOR_RESTARTS",
69
+ "repo": "EPAM_PIPELINE_DOCTOR_REPO",
70
+ "budget": "EPAM_PIPELINE_DOCTOR_BUDGET_USD"
71
+ },
72
+ "restart": {
73
+ "args": [
74
+ "--resume"
75
+ ]
76
+ },
77
+ "model": {
78
+ "family": "sonnet",
79
+ "config": "{install}/orchestrations/config/llm-defaults.claude.json"
80
+ },
81
+ "logs": {
82
+ "dir": "doctor",
83
+ "alerts": "abort-alerts.log",
84
+ "evidence": [
85
+ "driver.log"
86
+ ],
87
+ "evidenceTailLines": 200
88
+ },
89
+ "branch": {
90
+ "prefix": "doctor/"
91
+ },
92
+ "boundaries": [
93
+ "the ticket (any AMSD-… story) and its Jira content",
94
+ "every client repository and every codeline copy the run works in (cx-shared, gotransit, metrolinx, upexpress and any other)",
95
+ "the bash pipeline (the install's orchestrations/ folder) — a defect there is reported, never repaired",
96
+ "package.json, bin/, docs/ and everything outside the repairable directories",
97
+ "the pipeline's own prompt templates, agent roster, seams, ladders and system instructions in the install's orchestrations/ folder (the generators' prompts in generator/ are the agentic layer the doctor may repair)"
98
+ ],
99
+ "agentic": {
100
+ "dirs": [
101
+ "generator",
102
+ "hooks"
103
+ ],
104
+ "abortSignatures": [
105
+ "session's answer is not JSON",
106
+ "the detective gave none",
107
+ "the step runner gave no result"
108
+ ],
109
+ "autoApply": true,
110
+ "description": "the workflow generators' prompts, system instructions and answer schemas (generator/), and the hooks that hand one agent's result to the next (hooks/)"
111
+ }
112
+ }
@@ -48,7 +48,7 @@ async function runReleaseLines(ctx, lane, clPrd, laneLog, laneEnv, canonical, ph
48
48
  const id = (JSON.parse(fs.readFileSync(linePrd, 'utf8')).stories[0] || {}).id;
49
49
  if (id) completeLineByCommit(env, wt, lineLog, linePrd, `${env.EPAM_BRANCH_PREFIX || ''}AI-${id}-${slug}`, log);
50
50
  }
51
- const done = fs.existsSync(linePrd) && (() => { const p = JSON.parse(fs.readFileSync(linePrd, 'utf8')); return p.stories.length > 0 && p.stories.every((s) => s.status === 'completed'); })();
51
+ const done = lineIsDone(linePrd);
52
52
  if (bash(env, 'resume_preserves', ['completed-stories']).status === 0 && done
53
53
  && !handler(env, 'spec-predates-plan', [linePrd, canonical || clPrd]).stdout.trim()) { // :96–106
54
54
  rerun = [];
@@ -104,6 +104,20 @@ function completeLineByCommit(env, wt, lineLog, linePrd, branch, log) {
104
104
  if (git(wt, 'rev-parse', '--verify', '--quiet', branch).status !== 0) return;
105
105
  if (git(wt, 'checkout', '-q', branch).status !== 0) { log(`[orch] could not check out ${branch} to read its committed work`); return; }
106
106
  bash({ ...env, PROJECT_ROOT: wt }, 'story_resume_completed_by_commit', [wt, lineLog, linePrd], { log });
107
+ // THE PIPELINE'S FUNCTION SETS completed ONLY; the line's done test reads status (lib/release-lines.sh:96), so a line
108
+ // completed by its commit read as not done, took the --reset path and was rewritten (LIVE-AMSD-2974-11, 2026-10-20).
109
+ // A Writer-completed story carries both; this one does too.
110
+ const p = JSON.parse(fs.readFileSync(linePrd, 'utf8'));
111
+ let changed = false;
112
+ for (const s of p.stories || []) if (s.completed === true && s.status !== 'completed' && s.status !== 'deprecated') { s.status = 'completed'; changed = true; }
113
+ if (changed) fs.writeFileSync(linePrd, JSON.stringify(p, null, 2));
107
114
  }
108
115
 
109
- module.exports = { runReleaseLines, completeLineByCommit };
116
+ /** A line whose every story is completed — the pipeline's own test (lib/release-lines.sh:96). */
117
+ function lineIsDone(linePrd) {
118
+ if (!fs.existsSync(linePrd)) return false;
119
+ const p = JSON.parse(fs.readFileSync(linePrd, 'utf8'));
120
+ return (p.stories || []).length > 0 && p.stories.every((s) => s.status === 'completed');
121
+ }
122
+
123
+ module.exports = { runReleaseLines, completeLineByCommit, lineIsDone };
package/driver/run.js CHANGED
@@ -578,6 +578,17 @@ if (require.main === module) {
578
578
  if (r.error) { process.stderr.write(`the stand-in runtime could not drop the network (unshare -rn): ${r.error.message}\n`); process.exit(1); }
579
579
  process.exit(r.status === null ? 1 : r.status);
580
580
  }
581
+ // THE CRASH LISTENER is the shared component's (lib/doctor/crash-record.js), loaded from the install this driver runs against, writing the file this port's
582
+ // profile names: a throw nothing caught leaves that record and a `crashed` answer, and the launcher decides from it whether the failure is a defect in this
583
+ // code (the shared doctor's classifier). An install that predates the shared doctor is SAID, loudly — never silently unlistened.
584
+ const crash = (() => {
585
+ const f = path.join(path.resolve(a0.install || '.'), 'orchestrations', 'scripts', 'lib', 'doctor', 'crash-record.js');
586
+ try {
587
+ const lib = require(f); // eslint-disable-line global-require, import/no-dynamic-require
588
+ if (a0.out) lib.install({ runDir: path.resolve(a0.out), file: JSON.parse(fs.readFileSync(process.env.AMSD_DOCTOR_PROFILE || path.join(__dirname, '..', 'doctor', 'profile.json'), 'utf8')).crash.file });
589
+ return lib;
590
+ } catch (e) { process.stderr.write(`[driver] crash listener unavailable: ${e.code === 'MODULE_NOT_FOUND' ? `the install has no ${f} — it needs amsd-pipeline-shared >= 0.5.0 (amsd-pipeline-shared install --into <install>)` : e.message}\n`); return null; }
591
+ })();
581
592
  main(a0).then((o) => { process.stdout.write(`${JSON.stringify(o)}\n`); process.exit(o.exit || 0); })
582
- .catch((e) => { process.stderr.write(`${e.stack || e.message}\n`); process.exit(1); });
593
+ .catch((e) => { process.stderr.write(`${e.stack || e.message}\n`); if (crash) crash.die(e); else process.exit(1); });
583
594
  }
@@ -39,7 +39,7 @@ while IFS= read -r _f; do
39
39
  while IFS= read -r _pkg; do
40
40
  [ -z "$_pkg" ] && continue
41
41
  _vc="$PROJECT_ROOT/.contracts/vendor-${_pkg}.md"
42
- [ -f "$_vc" ] || _generate_vendor_contract "$PROJECT_ROOT" "$_pkg" 2>/dev/null
42
+ [ -f "$_vc" ] || _generate_vendor_contract "$PROJECT_ROOT" "$_pkg" >/dev/null 2>/dev/null
43
43
  [ -f "$_vc" ] && dependency_contracts="${dependency_contracts}
44
44
  ### Vendor package: ${_pkg}
45
45
  $(cat "$_vc")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amsd-pipeline-workflow",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
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": {
@@ -11,6 +11,7 @@
11
11
  "driver/",
12
12
  "generator/",
13
13
  "hooks/",
14
+ "doctor/",
14
15
  "mcp/",
15
16
  "test/layer1/harness.js",
16
17
  "test/layer1/hook-harness.js",