common-qa-mcp 0.4.0 → 0.5.0

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.
@@ -38,7 +38,7 @@ export class RemoteMcpAdapter {
38
38
  async client() {
39
39
  if (!this.clientPromise) {
40
40
  this.clientPromise = (async () => {
41
- const client = new Client({ name: 'common-qa-mcp', version: '0.4.0' });
41
+ const client = new Client({ name: 'common-qa-mcp', version: '0.5.0' });
42
42
  // The `as Transport` cast bridges exactOptionalPropertyTypes: StreamableHTTPClientTransport
43
43
  // implements Transport, but the SDK's own .d.ts (built without EOPT) leaves its optional
44
44
  // callbacks (onclose?/onerror?/onmessage?) without `| undefined`, which this project's EOPT
@@ -22,6 +22,7 @@ export declare class SimplifyQaAdapter {
22
22
  private readonly http;
23
23
  private agentHop;
24
24
  private formulaCache;
25
+ private backlogDefaultsCache;
25
26
  constructor(cfg: ConnectedQaConfig, transport?: SimplifyQaTransport);
26
27
  private headers;
27
28
  private requireProjectId;
@@ -89,6 +90,23 @@ export declare class SimplifyQaAdapter {
89
90
  listDefects(q: DefectQuery): Promise<ListResult<DefectSummary>>;
90
91
  /** `POST /pm/task/search` — same searchFields protocol as defect/userstory search. */
91
92
  listTasks(q: TaskQuery): Promise<ListResult<TaskSummary>>;
93
+ /** Fetch the project's default `state` and `priority` for a backlog entity.
94
+ * parentId: 1 = Epic, 2 = Feature, 3 = User Story. Two-source resolution, in order:
95
+ *
96
+ * 1. `/pm/fields/:parentId?fieldType=defaultFld` — pick the field value marked `isDefault: true`;
97
+ * fall back to the first non-archived value (seed ships with `isDefault: false` everywhere
98
+ * until a customer configures one).
99
+ * 2. If either state or priority is still missing, hit the entity's `/search` endpoint for one
100
+ * existing row in the project and steal its state / priority. Real data is a more reliable
101
+ * signal than the fields config, which is customer-scoped for Epic/Feature (parentIds 1/2)
102
+ * and can be missing entirely on customers that pre-date the seeding step.
103
+ *
104
+ * Returns the value's `.key` (uppercase enum like `PROPOSED`, `MEDIUM`) — that is what the entity
105
+ * row stores; the field metadata declares `keyname: "key"` / `type: "KEY"` and the SQA UI binding
106
+ * (see epic-form.component.html: `keyname="key" type="KEY" [displayKeys]="{'field':'value'}"`) confirms
107
+ * key is stored, `.value` is only for display. Both sources are wrapped in try/catch: a transient
108
+ * upstream error must not block create; the backend surfaces its own error if invalid. */
109
+ private getBacklogDefaults;
92
110
  createStory(input: StoryInput): Promise<SavedRef>;
93
111
  updateStory(id: string, patch: StoryUpdate): Promise<SavedRef>;
94
112
  listEpics(q: EpicQuery): Promise<ListResult<EpicSummary>>;
@@ -183,6 +201,7 @@ export declare class SimplifyQaAdapter {
183
201
  moduleId: number;
184
202
  copyTestData?: boolean;
185
203
  executionType?: 'AUTOMATION' | 'MANUAL';
204
+ versionId?: string | undefined;
186
205
  }): Promise<SavedRef>;
187
206
  /** Look up the freshly-created FN's version id — STEPSTOFN's response inlines the FN but not its
188
207
  * version, and the replace-in-TC step needs `fnVersionId`. Mirrors the FE
@@ -207,6 +226,7 @@ export declare class SimplifyQaAdapter {
207
226
  fnVersionId: string;
208
227
  fnModuleId: number;
209
228
  technologyType: string;
229
+ versionId?: string | undefined;
210
230
  }): Promise<SavedRef>;
211
231
  /** One-shot extract-to-function-and-replace. Chains STEPSTOFN → fetch fn version id → replace in
212
232
  * source TC. If step 2 or 3 fails after step 1 succeeded, the caller sees the fn id in the error
@@ -217,6 +237,7 @@ export declare class SimplifyQaAdapter {
217
237
  moduleId: number;
218
238
  copyTestData?: boolean;
219
239
  executionType?: 'AUTOMATION' | 'MANUAL';
240
+ versionId?: string | undefined;
220
241
  }): Promise<SavedRef & {
221
242
  fnId: string;
222
243
  fnVersionId: string;
@@ -408,21 +429,30 @@ export declare class SimplifyQaAdapter {
408
429
  startRun(target: RunTarget, opts: RunOptions): Promise<RunRef>;
409
430
  stopRun(runId: string): Promise<void>;
410
431
  listCloudRunners(): Promise<CloudRunner[]>;
432
+ /** Gather every identifier this process can claim: bare hostname, non-loopback MAC addresses,
433
+ * non-loopback IPs. Cached per-adapter — network interfaces do not change mid-session. */
434
+ private localIdsCache;
435
+ private getLocalIdentifiers;
436
+ /** Does a machineRegistration row belong to the machine this MCP is running on?
437
+ * A single positive match on hostname, MAC, or IP is enough — the local agent registers with
438
+ * ALL of these, but the surviving match on the SQA row depends on OS quirks (Windows writes a
439
+ * padded hostname, macOS writes `foo.local`, IPs shift on Wi-Fi handoff). Any one match is
440
+ * proof enough; we don't try to reconcile the others. */
441
+ private isThisMachine;
411
442
  listLocalAgents(): Promise<LocalAgent[]>;
412
443
  /**
413
- * Pick THE ACTING USER'S OWN active agent — never row [0] of a shared-tenant roster (which
414
- * would silently dispatch a local run to a teammate's box, and the run would sit queued until
415
- * TTL because it lands in a Redis key that no local agent on the caller's machine polls).
444
+ * Pick THE ACTING USER'S OWN agent — never row [0] of a shared-tenant roster (which would
445
+ * silently dispatch a local run to a teammate's box, and the run would sit queued until TTL
446
+ * because it lands in a Redis key that no local agent on the caller's machine polls).
416
447
  *
417
448
  * SQA's `machineRegistration` row carries no owner_user_id column (unlike walnut, which matches
418
- * the JWT `id` claim to `owner_user_id`), so we identify "your" agent by hostname: common-mcp
419
- * runs on the caller's own box in the typical MCP setup, and SQA `machineName` is either the
420
- * bare OS hostname or a bracketed display form (`USER.NAME [ Hostname.local ]`) — both contain
421
- * `os.hostname()`. Filter → active only → hostname substring match → return the single hit.
422
- * Zero/many matches refuse honestly; a silent guess would just repeat the exact bug we're fixing.
449
+ * the JWT `id` claim to `owner_user_id`), so we identify "your" agent by physical identity:
450
+ * hostname, MAC address, or IP — any one match against `os.networkInterfaces()` on the box this
451
+ * MCP is running on. When exactly one row is "this machine", pick it regardless of the DB
452
+ * `active` flag (that flag is stale by design — see listLocalAgents for the executionQueue bug).
423
453
  *
424
- * ponytail: hostname heuristic. Upgrade to owner-user-id match if the backend adds that column
425
- * to machineRegistration (mirror walnut's `resolveLocalAgentCredential` at that point).
454
+ * ponytail: multi-identifier heuristic. Upgrade to owner-user-id match if the backend adds that
455
+ * column to machineRegistration (mirror walnut's `resolveLocalAgentCredential` at that point).
426
456
  */
427
457
  private resolveLocalAgentId;
428
458
  private detectExecutionOS;
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/simplifyqa/adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/simplifyqa/adapter.ts"],"names":[],"mappings":"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"}
@@ -231,6 +231,9 @@ export class SimplifyQaAdapter {
231
231
  // (from the Promise.all in saveGeneratedIterations) all await the same fetch instead of each firing
232
232
  // their own /tm/syncFormulas/fetch before the cache is populated.
233
233
  this.formulaCache = null;
234
+ // Same rationale for the backlog default state/priority per parentId (1=Epic, 2=Feature, 3=Story).
235
+ // Fields config barely changes mid-session; cache the in-flight promise so parallel creates share it.
236
+ this.backlogDefaultsCache = new Map();
234
237
  // ---------------------------------------------------------------------- Automation resolvers
235
238
  /** POST /tm/action/search → per (technology, name) → row (id + objectRequired + objectTemplate).
236
239
  * Cached per adapter instance: the catalog is customer-wide and rarely changes during a session.
@@ -244,6 +247,9 @@ export class SimplifyQaAdapter {
244
247
  * formulaCache. Populated from `POST /tm/objectTemplate/search` (confirmed endpoint,
245
248
  * api-endpoints.ts `object.search`'s sibling `objectTemplate.search`). */
246
249
  this.objectTemplateCache = null;
250
+ /** Gather every identifier this process can claim: bare hostname, non-loopback MAC addresses,
251
+ * non-loopback IPs. Cached per-adapter — network interfaces do not change mid-session. */
252
+ this.localIdsCache = null;
247
253
  if (!cfg.baseUrl) {
248
254
  throw new Error('SimplifyQaAdapter requires a baseUrl — set QA_BASE_URL or use a JWT with a `url` claim.');
249
255
  }
@@ -738,12 +744,102 @@ export class SimplifyQaAdapter {
738
744
  const rows = pickArray(raw, 'data', 'content', 'tasks');
739
745
  return withTotal(rows.map(toTaskSummary), raw);
740
746
  }
747
+ // ---------------------------------------------------------------------- Backlog defaults
748
+ /** Fetch the project's default `state` and `priority` for a backlog entity.
749
+ * parentId: 1 = Epic, 2 = Feature, 3 = User Story. Two-source resolution, in order:
750
+ *
751
+ * 1. `/pm/fields/:parentId?fieldType=defaultFld` — pick the field value marked `isDefault: true`;
752
+ * fall back to the first non-archived value (seed ships with `isDefault: false` everywhere
753
+ * until a customer configures one).
754
+ * 2. If either state or priority is still missing, hit the entity's `/search` endpoint for one
755
+ * existing row in the project and steal its state / priority. Real data is a more reliable
756
+ * signal than the fields config, which is customer-scoped for Epic/Feature (parentIds 1/2)
757
+ * and can be missing entirely on customers that pre-date the seeding step.
758
+ *
759
+ * Returns the value's `.key` (uppercase enum like `PROPOSED`, `MEDIUM`) — that is what the entity
760
+ * row stores; the field metadata declares `keyname: "key"` / `type: "KEY"` and the SQA UI binding
761
+ * (see epic-form.component.html: `keyname="key" type="KEY" [displayKeys]="{'field':'value'}"`) confirms
762
+ * key is stored, `.value` is only for display. Both sources are wrapped in try/catch: a transient
763
+ * upstream error must not block create; the backend surfaces its own error if invalid. */
764
+ async getBacklogDefaults(parentId) {
765
+ const cached = this.backlogDefaultsCache.get(parentId);
766
+ if (cached)
767
+ return cached;
768
+ const projectId = this.requireProjectId();
769
+ const p = (async () => {
770
+ const out = {};
771
+ // Source 1: /pm/fields config.
772
+ try {
773
+ const raw = await this.http.getJson(`/pm/fields/${parentId}?fieldType=defaultFld&projectId=${projectId}`);
774
+ const fields = Array.isArray(raw) ? raw : pickArray(raw, 'data', 'content', 'fields');
775
+ const pick = (key) => {
776
+ const field = fields.find((f) => f?.key === key);
777
+ const values = Array.isArray(field?.values) ? field.values : [];
778
+ const chosen = values.find((v) => v?.isDefault === true && v?.archive !== true)
779
+ ?? values.find((v) => v?.archive !== true);
780
+ // Entity rows persist the uppercase enum (value.key). value.value is display-only.
781
+ const stored = str(chosen ?? {}, 'key', 'value');
782
+ return stored && stored.length > 0 ? stored : undefined;
783
+ };
784
+ const s = pick('state');
785
+ const pr = pick('priority');
786
+ if (s)
787
+ out.state = s;
788
+ if (pr)
789
+ out.priority = pr;
790
+ }
791
+ catch { /* fall through to Source 2 */ }
792
+ // Source 2: fall back to state/priority from an existing entity of the same kind in this project.
793
+ // Epic has no priority field, so we only need state for parentId=1.
794
+ const needsPriority = parentId !== 1 && !out.priority;
795
+ if (!out.state || needsPriority) {
796
+ try {
797
+ const path = parentId === 1 ? '/pm/epic/search'
798
+ : parentId === 2 ? '/pm/feature/search'
799
+ : '/pm/userstory/search';
800
+ // Story rows carry a scalar `projectId`; Epic/Feature rows carry an array `projectIds`.
801
+ const filters = [
802
+ { column: 'deleted', value: false },
803
+ parentId === 3
804
+ ? { column: 'projectId', value: projectId }
805
+ : { column: 'projectIds', value: projectId },
806
+ ];
807
+ const body = this.searchBody(filters, { startIndex: 0, limit: 1 });
808
+ const raw = await this.http.postJson(path, body);
809
+ const rows = pickArray(raw, 'data', 'content');
810
+ const row = rows[0];
811
+ if (row) {
812
+ if (!out.state) {
813
+ const s = str(row, 'state', 'status');
814
+ if (s)
815
+ out.state = s;
816
+ }
817
+ if (needsPriority) {
818
+ const pr = str(row, 'priority');
819
+ if (pr)
820
+ out.priority = pr;
821
+ }
822
+ }
823
+ }
824
+ catch { /* leave out.state / out.priority absent; caller can override */ }
825
+ }
826
+ return out;
827
+ })();
828
+ this.backlogDefaultsCache.set(parentId, p);
829
+ return p;
830
+ }
741
831
  // ---------------------------------------------------------------------- Stories (write)
742
832
  async createStory(input) {
833
+ const defaults = await this.getBacklogDefaults(3);
834
+ const state = input.state ?? defaults.state;
835
+ const priority = input.priority ?? defaults.priority;
743
836
  const body = {
744
837
  ...input,
745
838
  // SQA userstory expects a singular numeric projectId; fill from the connected project when omitted.
746
839
  projectId: input.projectId ?? this.requireProjectId(),
840
+ ...(state ? { state } : {}),
841
+ ...(priority ? { priority } : {}),
842
+ customFields: input.customFields ?? {},
747
843
  };
748
844
  const raw = await this.http.postJson('/pm/userstory', body);
749
845
  return toSavedRef(raw, true);
@@ -791,9 +887,13 @@ export class SimplifyQaAdapter {
791
887
  return toEpic(doc && typeof doc === 'object' ? doc : {});
792
888
  }
793
889
  async createEpic(input) {
890
+ const defaults = await this.getBacklogDefaults(1);
891
+ const state = input.state ?? defaults.state;
794
892
  const body = {
795
893
  ...input,
796
894
  projectIds: input.projectIds ?? [this.requireProjectId()],
895
+ ...(state ? { state } : {}),
896
+ customFields: input.customFields ?? {},
797
897
  };
798
898
  const raw = await this.http.postJson('/pm/epic', body);
799
899
  return toSavedRef(raw, true);
@@ -855,9 +955,15 @@ export class SimplifyQaAdapter {
855
955
  return toFeature(doc && typeof doc === 'object' ? doc : {});
856
956
  }
857
957
  async createFeature(input) {
958
+ const defaults = await this.getBacklogDefaults(2);
959
+ const state = input.state ?? defaults.state;
960
+ const priority = input.priority ?? defaults.priority;
858
961
  const body = {
859
962
  ...input,
860
963
  projectIds: input.projectIds ?? [this.requireProjectId()],
964
+ ...(state ? { state } : {}),
965
+ ...(priority ? { priority } : {}),
966
+ customFields: input.customFields ?? {},
861
967
  };
862
968
  const raw = await this.http.postJson('/pm/feature', body);
863
969
  return toSavedRef(raw, true);
@@ -1245,14 +1351,15 @@ export class SimplifyQaAdapter {
1245
1351
  if (!rawTc || Object.keys(rawTc).length === 0) {
1246
1352
  throw new Error(`test case ${tcId} not found`);
1247
1353
  }
1248
- const sourceVersionId = str(rawTc, 'versionId', 'selectedVersionId') ??
1249
- (rawTc?.versionsDetails && typeof rawTc.versionsDetails === 'object'
1250
- ? str(rawTc.versionsDetails, 'id', '_id', 'versionId')
1251
- : undefined) ??
1252
- (Array.isArray(rawTc?.versions) && rawTc.versions.length > 0
1253
- ? str(rawTc.versions[0], 'id', '_id')
1254
- : undefined);
1255
- const rawSteps = await this.fetchStepsCollection(tcId, rawTc);
1354
+ const sourceVersionId = opts.versionId ||
1355
+ (str(rawTc, 'versionId', 'selectedVersionId') ??
1356
+ (rawTc?.versionsDetails && typeof rawTc.versionsDetails === 'object'
1357
+ ? str(rawTc.versionsDetails, 'id', '_id', 'versionId')
1358
+ : undefined) ??
1359
+ (Array.isArray(rawTc?.versions) && rawTc.versions.length > 0
1360
+ ? str(rawTc.versions[0], 'id', '_id')
1361
+ : undefined));
1362
+ const rawSteps = await this.fetchStepsCollection(tcId, rawTc, opts.versionId);
1256
1363
  const picked = rawSteps
1257
1364
  .filter((s) => seqSet.has(Number(s.seq)))
1258
1365
  .sort((a, b) => Number(a.seq) - Number(b.seq));
@@ -1392,23 +1499,32 @@ export class SimplifyQaAdapter {
1392
1499
  const rawTc = await this.fetchRawTestcase(tcId);
1393
1500
  if (!rawTc || Object.keys(rawTc).length === 0)
1394
1501
  throw new Error(`test case ${tcId} not found`);
1395
- const execType = String(str(rawTc, 'executionType') ?? '').toUpperCase();
1502
+ // `versionsDetailsRaw` reflects the TC's CURRENTLY-SELECTED version's inline details — only
1503
+ // trustworthy when opts.versionId targets that same version (the common case). When targeting a
1504
+ // DIFFERENT version, prefer that version's own row from rawTc.versions[] for name/executionType/
1505
+ // reviewApproveStatus below; syncONOFF isn't carried per-version there, so it falls back to ''.
1506
+ const versionsDetailsRaw = rawTc?.versionsDetails && typeof rawTc.versionsDetails === 'object' ? rawTc.versionsDetails : {};
1507
+ const currentVersionId = str(rawTc, 'versionId', 'selectedVersionId') ?? str(versionsDetailsRaw, 'id', '_id', 'versionId');
1508
+ const targetsCurrentVersion = !opts.versionId || opts.versionId === currentVersionId;
1509
+ const versionMatch = Array.isArray(rawTc?.versions)
1510
+ ? rawTc.versions.find((v) => str(v, 'id', '_id') === opts.versionId)
1511
+ : undefined;
1512
+ const execType = String((targetsCurrentVersion ? str(rawTc, 'executionType') : str(versionMatch, 'executionType')) ?? '').toUpperCase();
1396
1513
  if (execType === 'DUO') {
1397
1514
  throw new Error('DUO test cases are not yet supported for replaceInSource — split the extraction into AUTOMATION-only or MANUAL-only first.');
1398
1515
  }
1399
- const versionsDetailsRaw = rawTc?.versionsDetails && typeof rawTc.versionsDetails === 'object' ? rawTc.versionsDetails : {};
1400
- const syncOnOff = str(versionsDetailsRaw, 'syncONOFF');
1516
+ const syncOnOff = targetsCurrentVersion ? str(versionsDetailsRaw, 'syncONOFF') : undefined;
1401
1517
  if (syncOnOff === 'SYNCON') {
1402
1518
  throw new Error('SYNCON (mirrored automation/manual) test cases are not yet supported for replaceInSource.');
1403
1519
  }
1404
- const sourceVersionId = str(rawTc, 'versionId', 'selectedVersionId') ??
1405
- str(versionsDetailsRaw, 'id', '_id', 'versionId') ??
1520
+ const sourceVersionId = opts.versionId ||
1521
+ currentVersionId ||
1406
1522
  (Array.isArray(rawTc?.versions) && rawTc.versions.length > 0
1407
1523
  ? str(rawTc.versions[0], 'id', '_id')
1408
1524
  : undefined);
1409
1525
  if (!sourceVersionId)
1410
1526
  throw new Error(`test case ${tcId} has no versionId — cannot save`);
1411
- const rawSteps = await this.fetchStepsCollection(tcId, rawTc);
1527
+ const rawSteps = await this.fetchStepsCollection(tcId, rawTc, opts.versionId);
1412
1528
  // Refuse picks that would break brackets. FUNCTION/FUNCTION_END pairs must not be half-picked;
1413
1529
  // control-flow starts (IF/WHILE_START/FOR_START/…) must not span the range partially.
1414
1530
  const CONTROL_TYPES = new Set([
@@ -1503,18 +1619,16 @@ export class SimplifyQaAdapter {
1503
1619
  tcfnObj['projectId'] = typeof tcfnObj['projectId'] === 'number' ? tcfnObj['projectId'] : projectId;
1504
1620
  if (typeof tcfnObj['obsolete'] !== 'boolean')
1505
1621
  tcfnObj['obsolete'] = false;
1506
- // Backend joi validator refuses an empty versionsDetails.name (BADREQUEST from /tm/testcase). The
1507
- // name isn't always inlined on the raw TC's `versionsDetails` — for TCs opened via search or
1508
- // certain list endpoints, only `versionsDetails.id` is present. Walk the version list to find
1509
- // the row whose id matches sourceVersionId and use ITS name; fall back to 'v1.0' (the SQA
1510
- // default set by project.service.js when a version is first created).
1511
- let versionName = str(versionsDetailsRaw, 'name');
1512
- if (!versionName && Array.isArray(rawTc?.versions)) {
1513
- const match = rawTc.versions.find((v) => str(v, 'id', '_id') === sourceVersionId);
1514
- versionName = match ? str(match, 'name') : undefined;
1515
- }
1516
- if (!versionName)
1517
- versionName = 'v1.0';
1622
+ // Backend joi validator refuses an empty versionsDetails.name (BADREQUEST from /tm/testcase).
1623
+ // `versionsDetailsRaw` only describes the CURRENT version — when targeting a different one,
1624
+ // its name/reviewApproveStatus belong to the wrong version, so prefer `versionMatch` instead.
1625
+ // Fall back to 'v1.0' (the SQA default set by project.service.js) if nothing resolves.
1626
+ const versionName = (targetsCurrentVersion ? str(versionsDetailsRaw, 'name') : undefined) ??
1627
+ str(versionMatch, 'name') ??
1628
+ 'v1.0';
1629
+ const reviewApproveStatus = (targetsCurrentVersion ? str(versionsDetailsRaw, 'reviewApproveStatus') : undefined) ??
1630
+ str(versionMatch, 'reviewApproveStatus') ??
1631
+ '';
1518
1632
  const versionsDetails = {
1519
1633
  name: versionName,
1520
1634
  id: sourceVersionId,
@@ -1522,7 +1636,7 @@ export class SimplifyQaAdapter {
1522
1636
  syncONOFF: syncOnOff ?? '',
1523
1637
  executionType: execType || 'AUTOMATION',
1524
1638
  functionProjectIds: [projectId],
1525
- reviewApproveStatus: str(versionsDetailsRaw, 'reviewApproveStatus') ?? '',
1639
+ reviewApproveStatus,
1526
1640
  };
1527
1641
  const payload = {
1528
1642
  tcfnObj,
@@ -1556,7 +1670,7 @@ export class SimplifyQaAdapter {
1556
1670
  // Reuse tech detection from saveStepsAsFunction (same rule: single tech across picks → that
1557
1671
  // tech, else fall back to the source TC's tcType).
1558
1672
  const rawTc = await this.fetchRawTestcase(tcId);
1559
- const rawSteps = await this.fetchStepsCollection(tcId, rawTc);
1673
+ const rawSteps = await this.fetchStepsCollection(tcId, rawTc, opts.versionId);
1560
1674
  const picked = rawSteps.filter((s) => opts.stepSeqs.includes(Number(s.seq)));
1561
1675
  const techs = new Set(picked.map((s) => str(s, 'technologyType')).filter(Boolean));
1562
1676
  const technologyType = techs.size === 1 ? [...techs][0] : (str(rawTc, 'tcType') ?? 'WEB');
@@ -1567,6 +1681,7 @@ export class SimplifyQaAdapter {
1567
1681
  fnVersionId,
1568
1682
  fnModuleId: opts.moduleId,
1569
1683
  technologyType,
1684
+ versionId: opts.versionId,
1570
1685
  });
1571
1686
  return { ...tcRef, fnId: fnRef.id, fnVersionId };
1572
1687
  }
@@ -2946,6 +3061,46 @@ export class SimplifyQaAdapter {
2946
3061
  };
2947
3062
  });
2948
3063
  }
3064
+ getLocalIdentifiers() {
3065
+ if (this.localIdsCache)
3066
+ return this.localIdsCache;
3067
+ const hostFull = os.hostname().toLowerCase();
3068
+ const hostBare = hostFull.replace(/\.(local|lan|localdomain)$/, '').split('.')[0] ?? hostFull;
3069
+ const macs = new Set();
3070
+ const ips = new Set();
3071
+ const ifaces = os.networkInterfaces();
3072
+ for (const list of Object.values(ifaces)) {
3073
+ if (!list)
3074
+ continue;
3075
+ for (const iface of list) {
3076
+ if (iface.internal)
3077
+ continue;
3078
+ if (iface.mac && iface.mac !== '00:00:00:00:00:00')
3079
+ macs.add(iface.mac.toLowerCase());
3080
+ if (iface.address)
3081
+ ips.add(iface.address);
3082
+ }
3083
+ }
3084
+ this.localIdsCache = { hostBare, macs, ips };
3085
+ return this.localIdsCache;
3086
+ }
3087
+ /** Does a machineRegistration row belong to the machine this MCP is running on?
3088
+ * A single positive match on hostname, MAC, or IP is enough — the local agent registers with
3089
+ * ALL of these, but the surviving match on the SQA row depends on OS quirks (Windows writes a
3090
+ * padded hostname, macOS writes `foo.local`, IPs shift on Wi-Fi handoff). Any one match is
3091
+ * proof enough; we don't try to reconcile the others. */
3092
+ isThisMachine(row, ids = this.getLocalIdentifiers()) {
3093
+ const name = (str(row, 'machineName', 'name', 'host') ?? '').toLowerCase();
3094
+ if (ids.hostBare.length > 0 && name.length > 0 && name.includes(ids.hostBare))
3095
+ return true;
3096
+ const mac = (str(row, 'macAddress') ?? '').toLowerCase();
3097
+ if (mac.length > 0 && ids.macs.has(mac))
3098
+ return true;
3099
+ const ip = str(row, 'ipAddress', 'hostAddress') ?? '';
3100
+ if (ip.length > 0 && ids.ips.has(ip))
3101
+ return true;
3102
+ return false;
3103
+ }
2949
3104
  async listLocalAgents() {
2950
3105
  // ponytail: no projectId filter — machine_registration.projectId is never written by
2951
3106
  // test-management/services/machine_registration.service.js (create only stamps
@@ -2954,50 +3109,43 @@ export class SimplifyQaAdapter {
2954
3109
  const body = this.searchBody([{ column: 'deleted', value: false }], { limit: 1000 });
2955
3110
  const raw = await this.http.postJson('/tm/machineRegistration/search', body);
2956
3111
  const rows = pickArray(raw, 'data', 'content', 'agents', 'machines');
2957
- // ponytail: hostname override, same trick sqa-ui uses at
2958
- // sqa-ui/.../machine-registration.component.ts:169. The DB `active` flag is only
2959
- // written to `true` on first registration (fire-and-forget in executions/services/
2960
- // executionQueue.service.js:49) and back to `false` by a 7s-stale cron. Every other poll
2961
- // is a no-op, so a still-polling agent frequently shows `active:false`. When the row's
2962
- // machineName matches THIS box's hostname, MCP is running on the same machine as the
2963
- // agent — trust that over the stale DB flag. Upgrade path: if the backend adds a live
2964
- // Redis-presence endpoint (ZSCORE SQAAGENTTRACKER), consult that here instead.
2965
- const hostFull = os.hostname().toLowerCase();
2966
- // Bare hostname: strip trailing .local/.lan/.localdomain AND take the first label. Handles
2967
- // both directions of "short vs FQDN" mismatch — the agent may register as `Pratiks-MBP`
2968
- // while os.hostname() returns `pratiks-mbp.local`, or vice-versa.
2969
- const hostBare = hostFull.replace(/\.(local|lan|localdomain)$/, '').split('.')[0] ?? hostFull;
3112
+ // ponytail: mirror sqa-ui/machine-registration.component.ts:169. The DB `active` flag is only
3113
+ // written to `true` on FIRST registration (fire-and-forget in executions/services/
3114
+ // executionQueue.service.js:49) and back to `false` by a 7s-stale cron. Every subsequent poll
3115
+ // is a no-op for the DB — a still-polling agent frequently shows `active:false`. Executions
3116
+ // service is off-limits for the fix, so when the row belongs to THIS machine (any of hostname
3117
+ // / MAC / IP matches), trust that over the stale DB flag.
3118
+ const ids = this.getLocalIdentifiers();
2970
3119
  return rows.map((n) => {
2971
3120
  const id = str(n, 'id', '_id', 'agentId') ?? '';
2972
3121
  const agentName = str(n, 'machineName', 'name', 'host');
2973
3122
  const owner = str(n, 'owner', 'userName', 'createdBy');
2974
3123
  const hostAddr = str(n, 'macAddress', 'hostAddress');
2975
3124
  const dbActive = typeof n?.active === 'boolean' ? n.active : false;
2976
- const nameLc = agentName?.toLowerCase() ?? '';
2977
- const isMyBox = nameLc.length > 0 && hostBare.length > 0 && nameLc.includes(hostBare);
3125
+ const isMyBox = this.isThisMachine(n, ids);
2978
3126
  return {
2979
3127
  id,
2980
3128
  ...(agentName ? { name: agentName } : {}),
2981
3129
  online: dbActive || isMyBox,
3130
+ ...(isMyBox ? { isThisMachine: true } : {}),
2982
3131
  ...(owner ? { owner } : {}),
2983
3132
  ...(hostAddr ? { host: hostAddr } : {}),
2984
3133
  };
2985
3134
  });
2986
3135
  }
2987
3136
  /**
2988
- * Pick THE ACTING USER'S OWN active agent — never row [0] of a shared-tenant roster (which
2989
- * would silently dispatch a local run to a teammate's box, and the run would sit queued until
2990
- * TTL because it lands in a Redis key that no local agent on the caller's machine polls).
3137
+ * Pick THE ACTING USER'S OWN agent — never row [0] of a shared-tenant roster (which would
3138
+ * silently dispatch a local run to a teammate's box, and the run would sit queued until TTL
3139
+ * because it lands in a Redis key that no local agent on the caller's machine polls).
2991
3140
  *
2992
3141
  * SQA's `machineRegistration` row carries no owner_user_id column (unlike walnut, which matches
2993
- * the JWT `id` claim to `owner_user_id`), so we identify "your" agent by hostname: common-mcp
2994
- * runs on the caller's own box in the typical MCP setup, and SQA `machineName` is either the
2995
- * bare OS hostname or a bracketed display form (`USER.NAME [ Hostname.local ]`) — both contain
2996
- * `os.hostname()`. Filter → active only → hostname substring match → return the single hit.
2997
- * Zero/many matches refuse honestly; a silent guess would just repeat the exact bug we're fixing.
3142
+ * the JWT `id` claim to `owner_user_id`), so we identify "your" agent by physical identity:
3143
+ * hostname, MAC address, or IP — any one match against `os.networkInterfaces()` on the box this
3144
+ * MCP is running on. When exactly one row is "this machine", pick it regardless of the DB
3145
+ * `active` flag (that flag is stale by design — see listLocalAgents for the executionQueue bug).
2998
3146
  *
2999
- * ponytail: hostname heuristic. Upgrade to owner-user-id match if the backend adds that column
3000
- * to machineRegistration (mirror walnut's `resolveLocalAgentCredential` at that point).
3147
+ * ponytail: multi-identifier heuristic. Upgrade to owner-user-id match if the backend adds that
3148
+ * column to machineRegistration (mirror walnut's `resolveLocalAgentCredential` at that point).
3001
3149
  */
3002
3150
  async resolveLocalAgentId(preferred) {
3003
3151
  if (preferred && preferred.length > 0)
@@ -3007,24 +3155,25 @@ export class SimplifyQaAdapter {
3007
3155
  throw new Error('startRun (LOCAL): no local agent registered. Register a machine in the SimplifyQA UI ' +
3008
3156
  'first, or pass mode: "cloud" if a cloud runner is provisioned.');
3009
3157
  }
3010
- const active = agents.filter((a) => a.online);
3011
- if (active.length === 0) {
3012
- throw new Error('startRun (LOCAL): no local agent is currently active (polling). Launch your SimplifyQA ' +
3013
- 'desktop agent and retry — or pass `agentId` explicitly if you know the credential id.');
3014
- }
3015
- const hostFull = os.hostname().toLowerCase();
3016
- // Same short-vs-FQDN handling as listLocalAgents — see the ponytail note there.
3017
- const host = hostFull.replace(/\.(local|lan|localdomain)$/, '').split('.')[0] ?? hostFull;
3018
- const mine = active.filter((a) => host.length > 0 && (a.name ?? '').toLowerCase().includes(host));
3158
+ // Priority 1: rows that physically match this machine. Ignore the DB `active` flag — a running
3159
+ // agent frequently shows `active:false` between the 5s stale-cron and the next heartbeat, and
3160
+ // the executions service is off-limits for the real fix.
3161
+ const mine = agents.filter((a) => a.isThisMachine);
3019
3162
  if (mine.length === 1)
3020
3163
  return mine[0].id;
3021
- const listing = (arr) => arr.map((a) => ` - ${a.name ?? a.id} \`${a.id}\``).join('\n');
3022
3164
  if (mine.length > 1) {
3023
- throw new Error(`startRun (LOCAL): multiple active agents match this machine (${host}). Pass \`agentId\` ` +
3024
- `to pick one:\n${listing(mine)}`);
3025
- }
3026
- throw new Error(`startRun (LOCAL): could not auto-pick your local agent — no active agent's name matches ` +
3027
- `this hostname (${host}). Pass \`agentId\` explicitly. Active agents:\n${listing(active)}`);
3165
+ const listing = mine.map((a) => ` - ${a.name ?? a.id} \`${a.id}\``).join('\n');
3166
+ throw new Error(`startRun (LOCAL): multiple agents registered from this machine. Pass \`agentId\` to ` +
3167
+ `pick one:\n${listing}`);
3168
+ }
3169
+ // No physical match — fall back to the old online-only pool (roster shared across teammates).
3170
+ // No physical match for this machine. Refuse to guess — dispatching to a teammate's agent is
3171
+ // the exact silent-misroute bug isThisMachine was written to prevent. Surface the roster so
3172
+ // the caller can pass `agentId` explicitly.
3173
+ const listing = agents.map((a) => ` - ${a.name ?? a.id} \`${a.id}\`${a.online ? '' : ' (offline)'}`).join('\n');
3174
+ throw new Error(`startRun (LOCAL): no agent registered from this machine (hostname / MAC / IP did not match ` +
3175
+ `any row). Launch the SimplifyQA desktop agent on this box, or pass \`agentId\` explicitly. ` +
3176
+ `Registered agents:\n${listing}`);
3028
3177
  }
3029
3178
  detectExecutionOS() {
3030
3179
  if (process.platform === 'win32')