@papi-ai/server 0.7.56 → 0.7.58

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/dist/index.js CHANGED
@@ -8081,7 +8081,9 @@ import {
8081
8081
  // src/universal-frame.ts
8082
8082
  var UNIVERSAL_FRAME = `PAPI gives this project a structured plan \u2192 build \u2192 review cycle, persisted across sessions. Follow it:
8083
8083
 
8084
- 1. ORIENT FIRST. At the start of every session, call \`orient\` (or \`papi\`) before anything else \u2014 it returns the current cycle, what's in flight, and the recommended next action. Re-run it after any context compression.
8084
+ 0. NEW HERE? If PAPI has never been set up for this project (no cycles yet, or \`orient\` says the board is empty), call \`setup\` FIRST \u2014 it generates the Product Brief and scaffolds the workflow. Then run \`plan\` to create the first cycle. This is the required first step for a brand-new project; everything below assumes setup has already run.
8085
+
8086
+ 1. ORIENT FIRST (once set up). At the start of every session, call \`orient\` (or \`papi\`) before anything else \u2014 it returns the current cycle, what's in flight, and the recommended next action. Re-run it after any context compression.
8085
8087
 
8086
8088
  2. THE CYCLE, IN ORDER: \`plan\` (once per cycle) \u2192 \`build_list\` (pick a task) \u2192 \`build_execute <task>\` to start \u2192 implement the task from its BUILD HANDOFF \u2192 \`build_execute\` again to complete with a build report \u2192 \`review_submit\` \u2192 \`release\` when every cycle task is done.
8087
8089
 
@@ -8425,6 +8427,21 @@ function computeCycleEffort(cycleTaskRows, cycleReports) {
8425
8427
  function velocityPoints(v) {
8426
8428
  return v.deliveredPoints ?? v.effortPoints;
8427
8429
  }
8430
+ function resolveCompletedCycleDates(existingStart, earliestActivity, now) {
8431
+ const startDate = existingStart || earliestActivity || now;
8432
+ const startMs = Date.parse(startDate);
8433
+ const endMs = Date.parse(now);
8434
+ const known = Number.isFinite(startMs) && Number.isFinite(endMs) && startMs < endMs;
8435
+ return { startDate, endDate: known ? now : null };
8436
+ }
8437
+ function earliestCycleActivity(reports) {
8438
+ let earliest;
8439
+ for (const r of reports) {
8440
+ const ts = r.startedAt || r.createdAt || r.date;
8441
+ if (ts && (!earliest || Date.parse(ts) < Date.parse(earliest))) earliest = ts;
8442
+ }
8443
+ return earliest;
8444
+ }
8428
8445
  function computeSnapshotsFromBuildReports(reports, tasks) {
8429
8446
  const reportsByCycle = /* @__PURE__ */ new Map();
8430
8447
  for (const r of reports) {
@@ -8826,7 +8843,7 @@ PRE-BUILD VERIFICATION
8826
8843
  [List 2-5 specific file paths the builder should read BEFORE implementing to check if the functionality already exists. Derive these from FILES LIKELY TOUCHED \u2014 pick the files most likely to already contain the target functionality. ALSO mandate a docs sweep, not just file-existence (task-2161): name any docs the builder should check via doc_search or the docs index \u2014 a design/research/status:final doc or a prior task may already cover the work. If >80% of the scope is already implemented, the builder should report "already built" instead of re-implementing. Include this section for EVERY task \u2014 it prevents wasted build slots on already-shipped code.]
8827
8844
 
8828
8845
  FILES LIKELY TOUCHED
8829
- [files]
8846
+ [files \u2014 real paths only. TEST FILE LOCATIONS (task-2606): PAPI tests are NOT co-located next to source in a src/**/__tests__/ folder. Use the actual layout: server tests \u2192 packages/server/tests/<name>.test.ts; adapter-pg tests \u2192 packages/adapter-pg/src/__tests__/<name>.test.ts; dashboard/root tests \u2192 tests/<area>/<name>.test.ts. Do NOT invent packages/server/src/**/__tests__/ paths \u2014 they do not exist.]
8830
8847
 
8831
8848
  EFFORT
8832
8849
  [XS/S/M/L/XL]
@@ -9286,7 +9303,14 @@ function buildPlanUserMessage(ctx) {
9286
9303
  parts.push("### Pre-Assigned Tasks", "", ctx.preAssignedTasks, "");
9287
9304
  }
9288
9305
  if (ctx.codebaseScan) {
9289
- parts.push("### Codebase Scan (existing implementations)", "", ctx.codebaseScan, "");
9306
+ parts.push(
9307
+ "### Codebase Scan (existing implementations)",
9308
+ "",
9309
+ "Any task tagged **PREMISE-UNVERIFIED** is a discovery/auto-triaged item whose premise references code that already exists \u2014 before scheduling it, verify the premise still holds against the live code; if it is already resolved, deprioritise or cancel it rather than spending a build slot re-verifying shipped work.",
9310
+ "",
9311
+ ctx.codebaseScan,
9312
+ ""
9313
+ );
9290
9314
  }
9291
9315
  if (ctx.buildPatterns) {
9292
9316
  parts.push("### Build Patterns", "", ctx.buildPatterns, "");
@@ -9983,7 +10007,7 @@ REFERENCE DOCS
9983
10007
  [Optional \u2014 paths to docs/ files with background context. Omit if not needed.]
9984
10008
 
9985
10009
  FILES LIKELY TOUCHED
9986
- [files]
10010
+ [files \u2014 real paths only. TEST FILE LOCATIONS (task-2606): PAPI tests are NOT co-located next to source in a src/**/__tests__/ folder. Use the actual layout: server tests \u2192 packages/server/tests/<name>.test.ts; adapter-pg tests \u2192 packages/adapter-pg/src/__tests__/<name>.test.ts; dashboard/root tests \u2192 tests/<area>/<name>.test.ts. Do NOT invent packages/server/src/**/__tests__/ paths \u2014 they do not exist.]
9987
10011
 
9988
10012
  EFFORT
9989
10013
  [XS/S/M/L/XL]`;
@@ -10699,6 +10723,10 @@ function extractSearchTerms(title, notes) {
10699
10723
  }
10700
10724
  return terms.slice(0, 8);
10701
10725
  }
10726
+ function isDiscoveryTask(task) {
10727
+ if (task.taskType === "discovery") return true;
10728
+ return /^\s*\[auto-triaged\]/i.test(task.title);
10729
+ }
10702
10730
  function grepForTerm(projectRoot, term) {
10703
10731
  try {
10704
10732
  const result = execSync2(
@@ -10732,19 +10760,24 @@ function scanCodebaseForTasks(projectRoot, tasks) {
10732
10760
  }
10733
10761
  }
10734
10762
  if (matches.length > 0) {
10735
- results.push({ taskId: task.id, terms, matches });
10763
+ results.push({ taskId: task.id, terms, matches, premiseUnverified: isDiscoveryTask(task) });
10736
10764
  }
10737
10765
  if (Date.now() - startTime > 5e3) break;
10738
10766
  }
10739
10767
  if (results.length === 0) return "";
10740
10768
  const elapsed = Date.now() - startTime;
10741
10769
  console.error(`[codebase-scan] scanned ${tasks.length} tasks in ${elapsed}ms \u2014 ${results.length} with matches`);
10770
+ const flaggedCount = results.filter((r) => r.premiseUnverified).length;
10771
+ const flagNote = flaggedCount > 0 ? ` \u2014 ${flaggedCount} discovery/auto-triaged task(s) flagged PREMISE-UNVERIFIED (verify before scheduling)` : "";
10742
10772
  const lines = [
10743
- `Codebase scan found existing implementations for ${results.length}/${tasks.length} candidate tasks (${elapsed}ms):`,
10773
+ `Codebase scan found existing implementations for ${results.length}/${tasks.length} candidate tasks (${elapsed}ms)${flagNote}:`,
10744
10774
  ""
10745
10775
  ];
10746
10776
  for (const result of results) {
10747
10777
  lines.push(`**${result.taskId}:**`);
10778
+ if (result.premiseUnverified) {
10779
+ lines.push(` \u26A0 PREMISE-UNVERIFIED: discovery/auto-triaged task whose premise references code already present in the repo \u2014 verify the premise still holds; it may already be resolved.`);
10780
+ }
10748
10781
  for (const match of result.matches.slice(0, 3)) {
10749
10782
  const fileList = match.files.slice(0, 3).join(", ");
10750
10783
  const moreCount = match.files.length > 3 ? ` (+${match.files.length - 3} more)` : "";
@@ -12628,7 +12661,7 @@ async function preparePlan(adapter2, config2, filters, focus, force, handoffsOnl
12628
12661
  t = startTimer();
12629
12662
  try {
12630
12663
  const scanTasks = await adapter2.queryBoard({ status: ["Backlog", "In Cycle", "Ready"], compact: true });
12631
- const candidates = scanTasks.filter((task) => task.priority !== "P3 Low" && task.scopeClass !== "brief").slice(0, 15).map((task) => ({ id: task.id, title: task.title, notes: task.notes }));
12664
+ const candidates = scanTasks.filter((task) => task.priority !== "P3 Low" && task.scopeClass !== "brief").slice(0, 15).map((task) => ({ id: task.id, title: task.title, notes: task.notes, taskType: task.taskType }));
12632
12665
  const scanResult = scanCodebaseForTasks(config2.projectRoot, candidates);
12633
12666
  if (scanResult) context.codebaseScan = scanResult;
12634
12667
  } catch (err) {
@@ -18815,6 +18848,7 @@ init_telemetry();
18815
18848
  import { writeFile as writeFile3, readFile as readFile5 } from "fs/promises";
18816
18849
  import { join as join10 } from "path";
18817
18850
  import { execFileSync as execFileSync4 } from "child_process";
18851
+ import { isSensitiveChangelogLine } from "@papi-ai/shared";
18818
18852
  init_git();
18819
18853
  var INITIAL_RELEASE_NOTES = `# Changelog
18820
18854
 
@@ -18826,7 +18860,9 @@ All notable changes to this project are documented in this file.
18826
18860
  `;
18827
18861
  function generateChangelogSection(version, commits) {
18828
18862
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
18829
- const filtered = commits.filter((c) => !new RegExp(`^[a-f0-9]+ release: ${version}$`).test(c));
18863
+ const filtered = commits.filter(
18864
+ (c) => !new RegExp(`^[a-f0-9]+ release: ${version}$`).test(c) && !isSensitiveChangelogLine(c)
18865
+ );
18830
18866
  const commitList = filtered.map((c) => `- ${c}`).join("\n");
18831
18867
  return `## ${version} \u2014 ${date}
18832
18868
 
@@ -19056,20 +19092,11 @@ To override, pass force=true (emits a telemetry warning).`
19056
19092
  existing = (await adapter2.readCycles()).find((c) => c.number === currentCycle);
19057
19093
  } catch {
19058
19094
  }
19059
- const completedCycle = {
19060
- id: `cycle-${currentCycle}`,
19061
- number: currentCycle,
19062
- status: "complete",
19063
- startDate: existing?.startDate ?? (/* @__PURE__ */ new Date()).toISOString(),
19064
- endDate: (/* @__PURE__ */ new Date()).toISOString(),
19065
- goals: existing?.goals ?? [],
19066
- boardHealth: existing?.boardHealth ?? "",
19067
- taskIds: existing?.taskIds ?? []
19068
- };
19069
19095
  let snapshot;
19096
+ let cycleReports = [];
19070
19097
  if (adapter2.getBuildReportsSince) {
19071
19098
  try {
19072
- const cycleReports = (await adapter2.getBuildReportsSince(currentCycle)).filter((r) => r.cycle === currentCycle);
19099
+ cycleReports = (await adapter2.getBuildReportsSince(currentCycle)).filter((r) => r.cycle === currentCycle);
19073
19100
  const cycleTasks = (await adapter2.queryBoard({ cycleSince: currentCycle, compact: true })).filter((t) => t.cycle === currentCycle);
19074
19101
  [snapshot] = computeSnapshotsFromBuildReports(cycleReports, cycleTasks);
19075
19102
  } catch (err) {
@@ -19078,6 +19105,22 @@ To override, pass force=true (emits a telemetry warning).`
19078
19105
  );
19079
19106
  }
19080
19107
  }
19108
+ const releaseNow = (/* @__PURE__ */ new Date()).toISOString();
19109
+ const { startDate: resolvedStart, endDate: resolvedEnd } = resolveCompletedCycleDates(
19110
+ existing?.startDate,
19111
+ earliestCycleActivity(cycleReports),
19112
+ releaseNow
19113
+ );
19114
+ const completedCycle = {
19115
+ id: `cycle-${currentCycle}`,
19116
+ number: currentCycle,
19117
+ status: "complete",
19118
+ startDate: resolvedStart,
19119
+ endDate: resolvedEnd ?? void 0,
19120
+ goals: existing?.goals ?? [],
19121
+ boardHealth: existing?.boardHealth ?? "",
19122
+ taskIds: existing?.taskIds ?? []
19123
+ };
19081
19124
  if (typeof adapter2.commitRelease === "function") {
19082
19125
  try {
19083
19126
  await adapter2.commitRelease({ cycle: completedCycle, snapshot: snapshot ?? null });
@@ -25782,6 +25825,50 @@ function countStalledP1(warnings) {
25782
25825
  const matches = stallLine.match(/task-\d+/g);
25783
25826
  return matches ? matches.length : 0;
25784
25827
  }
25828
+ function truncateTaskTitle(title, max = 55) {
25829
+ const t = (title ?? "").trim();
25830
+ if (!t) return "untitled";
25831
+ if (t.length <= max) return t;
25832
+ return t.slice(0, max - 1).trimEnd() + "\u2026";
25833
+ }
25834
+ async function resolveCarryForwardRefs(carryForward, tasks, adapter2) {
25835
+ const refs = /* @__PURE__ */ new Map();
25836
+ const ids = carryForward?.match(/\btask-\d+\b/g);
25837
+ if (!ids) return refs;
25838
+ const unique = [...new Set(ids)];
25839
+ const byId = new Map(tasks.filter((t) => t.displayId).map((t) => [t.displayId, t]));
25840
+ const missing = [];
25841
+ for (const id of unique) {
25842
+ const t = byId.get(id);
25843
+ if (t) refs.set(id, { title: t.title, status: t.status });
25844
+ else missing.push(id);
25845
+ }
25846
+ if (missing.length > 0 && adapter2?.getTasks) {
25847
+ try {
25848
+ const fetched = await adapter2.getTasks(missing);
25849
+ for (const t of fetched) {
25850
+ if (t.displayId) refs.set(t.displayId, { title: t.title, status: t.status });
25851
+ }
25852
+ } catch {
25853
+ }
25854
+ }
25855
+ return refs;
25856
+ }
25857
+ function annotateTaskRefs(text, refs) {
25858
+ if (!text || refs.size === 0) return text;
25859
+ const RESOLVED = /* @__PURE__ */ new Set(["Done", "Cancelled"]);
25860
+ return text.replace(/\btask-\d+\b(?!\s*[(—–-])/g, (id, offset, full) => {
25861
+ const ref = refs.get(id);
25862
+ if (!ref) return id;
25863
+ const rest = full.slice(offset + id.length);
25864
+ if (/^\s*[(—–-]/.test(rest)) return id;
25865
+ const resolved = ref.status && RESOLVED.has(ref.status);
25866
+ const titlePart = ref.title ? ` \u2014 ${truncateTaskTitle(ref.title)}` : "";
25867
+ const statusPart = resolved ? ` [\u2713 ${ref.status}]` : "";
25868
+ if (!titlePart && !statusPart) return id;
25869
+ return `${id}${titlePart}${statusPart}`;
25870
+ });
25871
+ }
25785
25872
  function formatSynthesisParagraph(opts) {
25786
25873
  const parts = [];
25787
25874
  parts.push(
@@ -25801,7 +25888,7 @@ function formatSynthesisParagraph(opts) {
25801
25888
  parts.push(`Next: ${cleanMode}`);
25802
25889
  return parts.join(" ");
25803
25890
  }
25804
- function formatOrientSummary(health, buildInfo, hierarchy, latestTag, projectRoot, environment = "unknown", subAgents = [], projectName, teamSummary, clientName) {
25891
+ function formatOrientSummary(health, buildInfo, hierarchy, latestTag, projectRoot, environment = "unknown", subAgents = [], projectName, teamSummary, clientName, taskRefs = /* @__PURE__ */ new Map()) {
25805
25892
  const lines = [];
25806
25893
  const cycleIsComplete = health.latestCycleStatus === "complete";
25807
25894
  const tagSuffix = latestTag ? ` \u2014 ${latestTag}` : "";
@@ -25944,7 +26031,7 @@ function formatOrientSummary(health, buildInfo, hierarchy, latestTag, projectRoo
25944
26031
  const hasCarryForward = health.carryForward !== "None found" && !health.carryForward.startsWith("No carry-forward");
25945
26032
  if (hasCarryForward) {
25946
26033
  lines.push("## Carry-Forward");
25947
- lines.push(health.carryForward);
26034
+ lines.push(annotateTaskRefs(health.carryForward, taskRefs));
25948
26035
  lines.push("");
25949
26036
  }
25950
26037
  const hasMetrics = health.metricsSection !== "Could not read methodology metrics." && !health.metricsSection.includes("undefined");
@@ -26233,7 +26320,7 @@ async function handleOrient(adapter2, config2, args = {}, clientName) {
26233
26320
  (t) => t.createdCycle != null && currentCycle - t.createdCycle >= 3
26234
26321
  );
26235
26322
  if (stalledP1.length === 0) return void 0;
26236
- const ids = stalledP1.map((t) => `${t.displayId} (${currentCycle - (t.createdCycle ?? currentCycle)}+ cycles)`).join(", ");
26323
+ const ids = stalledP1.map((t) => `${t.displayId} \u2014 ${truncateTaskTitle(t.title)} (${currentCycle - (t.createdCycle ?? currentCycle)}+ cycles)`).join(", ");
26237
26324
  return `\u26A0\uFE0F P1 tasks stalled 3+ cycles: ${ids}`;
26238
26325
  }),
26239
26326
  // task-1917 (C280): Owner Action Queue surface. Counts the user's open
@@ -26251,10 +26338,12 @@ async function handleOrient(adapter2, config2, args = {}, clientName) {
26251
26338
  let dueSuffix = "";
26252
26339
  if (adapter2.countDueOwnerActions) {
26253
26340
  const due = await adapter2.countDueOwnerActions(userId, projectId);
26254
- const parts = [];
26255
- if (due.dueToday > 0) parts.push(`${due.dueToday} due today`);
26256
- if (due.overdue > 0) parts.push(`${due.overdue} overdue`);
26257
- if (parts.length > 0) dueSuffix = ` (${parts.join(", ")})`;
26341
+ if (due.urgent > 0) {
26342
+ const detail = [];
26343
+ if (due.overdue > 0) detail.push(`${due.overdue} overdue`);
26344
+ if (due.dueToday > 0) detail.push(`${due.dueToday} due today`);
26345
+ dueSuffix = detail.length > 0 ? ` (${due.urgent} need you now \u2014 ${detail.join(", ")})` : ` (${due.urgent} need you now)`;
26346
+ }
26258
26347
  }
26259
26348
  return `\u26A0\uFE0F ${count} action${count === 1 ? "" : "s"} waiting on you${dueSuffix} \u2014 open /hub to triage the Owner Action Queue.`;
26260
26349
  } catch {
@@ -26657,7 +26746,8 @@ ${section}`;
26657
26746
  ]);
26658
26747
  const teamSummary = [teamSummaryLine, releaseHistoryLine].filter(Boolean).join("\n") || void 0;
26659
26748
  const deepHint = deepHousekeeping ? "" : "\n\n*Tip: pass `full: true` for Research Signals + version-drift, or `deep_housekeeping: true` to also check orphaned branches, merged-but-In-Progress tasks, unrecorded commits, unregistered docs, and stale skill forks (implies `full`).*";
26660
- return textResponse(projectBannerNote + formatOrientSummary(healthResult, buildInfo, hierarchy, latestTag, config2.projectRoot, environment, subAgents, projectName, teamSummary, clientName) + unblockNote + deferredGateNote + feedbackResolvedNote + alertsNote + ttfvNote + reconciliationNote + mergedInProgressNote + unrecordedNote + unregisteredDocsNote + staleSkillsNote + researchSignalsNote + recsNote + pendingReviewNote + patternsNote + unactionedIssuesNote + skillProposalsNote + sessionGuidanceNote + versionNote + enrichmentNote + deliveryShapeNote + preBuildCheckNote + deepHint + enrichmentFilesSection);
26749
+ const carryForwardRefs = await resolveCarryForwardRefs(healthResult.carryForward, allTasks, adapter2);
26750
+ return textResponse(projectBannerNote + formatOrientSummary(healthResult, buildInfo, hierarchy, latestTag, config2.projectRoot, environment, subAgents, projectName, teamSummary, clientName, carryForwardRefs) + unblockNote + deferredGateNote + feedbackResolvedNote + alertsNote + ttfvNote + reconciliationNote + mergedInProgressNote + unrecordedNote + unregisteredDocsNote + staleSkillsNote + researchSignalsNote + recsNote + pendingReviewNote + patternsNote + unactionedIssuesNote + skillProposalsNote + sessionGuidanceNote + versionNote + enrichmentNote + deliveryShapeNote + preBuildCheckNote + deepHint + enrichmentFilesSection);
26661
26751
  } catch (err) {
26662
26752
  const message = err instanceof Error ? err.message : String(err);
26663
26753
  const isKnownFriendly = /^(Orient failed|Project not found|No project|Setup required)/i.test(message);
@@ -29143,6 +29233,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
29143
29233
  var BEARER_PREFIX = "papi_";
29144
29234
  var BEARER_REGEX = /^(papi_|papi_oauth_)[a-f0-9]{64}$/;
29145
29235
  var RESOURCE_METADATA_URL = process.env["PAPI_RESOURCE_METADATA_URL"] ?? "https://getpapi.ai/.well-known/oauth-protected-resource";
29236
+ var GLAMA_MAINTAINER_EMAIL = process.env["GLAMA_MAINTAINER_EMAIL"] ?? "cathal@getpapi.ai";
29146
29237
  var DASHBOARD_ORIGIN = process.env["PAPI_DASHBOARD_URL"] ?? "https://getpapi.ai";
29147
29238
  var MCP_RESOURCE_URL = process.env["NEXT_PUBLIC_MCP_URL"] ?? "https://mcp.getpapi.ai";
29148
29239
  var FRIENDLY_GET_HTML = `<!doctype html>
@@ -29288,6 +29379,20 @@ function startHttpTransport(opts) {
29288
29379
  );
29289
29380
  return;
29290
29381
  }
29382
+ if (req.method === "GET" && req.url === "/.well-known/glama.json") {
29383
+ res.writeHead(200, {
29384
+ "Content-Type": "application/json",
29385
+ "Cache-Control": "public, max-age=3600",
29386
+ ...cors
29387
+ });
29388
+ res.end(
29389
+ JSON.stringify({
29390
+ $schema: "https://glama.ai/mcp/schemas/connector.json",
29391
+ maintainers: [{ email: GLAMA_MAINTAINER_EMAIL }]
29392
+ })
29393
+ );
29394
+ return;
29395
+ }
29291
29396
  if (req.method === "GET" && req.url === "/.well-known/oauth-authorization-server") {
29292
29397
  res.writeHead(302, {
29293
29398
  Location: `${DASHBOARD_ORIGIN}/.well-known/oauth-authorization-server`,
package/dist/prompts.js CHANGED
@@ -69,7 +69,7 @@ PRE-BUILD VERIFICATION
69
69
  [List 2-5 specific file paths the builder should read BEFORE implementing to check if the functionality already exists. Derive these from FILES LIKELY TOUCHED \u2014 pick the files most likely to already contain the target functionality. ALSO mandate a docs sweep, not just file-existence (task-2161): name any docs the builder should check via doc_search or the docs index \u2014 a design/research/status:final doc or a prior task may already cover the work. If >80% of the scope is already implemented, the builder should report "already built" instead of re-implementing. Include this section for EVERY task \u2014 it prevents wasted build slots on already-shipped code.]
70
70
 
71
71
  FILES LIKELY TOUCHED
72
- [files]
72
+ [files \u2014 real paths only. TEST FILE LOCATIONS (task-2606): PAPI tests are NOT co-located next to source in a src/**/__tests__/ folder. Use the actual layout: server tests \u2192 packages/server/tests/<name>.test.ts; adapter-pg tests \u2192 packages/adapter-pg/src/__tests__/<name>.test.ts; dashboard/root tests \u2192 tests/<area>/<name>.test.ts. Do NOT invent packages/server/src/**/__tests__/ paths \u2014 they do not exist.]
73
73
 
74
74
  EFFORT
75
75
  [XS/S/M/L/XL]
@@ -529,7 +529,14 @@ function buildPlanUserMessage(ctx) {
529
529
  parts.push("### Pre-Assigned Tasks", "", ctx.preAssignedTasks, "");
530
530
  }
531
531
  if (ctx.codebaseScan) {
532
- parts.push("### Codebase Scan (existing implementations)", "", ctx.codebaseScan, "");
532
+ parts.push(
533
+ "### Codebase Scan (existing implementations)",
534
+ "",
535
+ "Any task tagged **PREMISE-UNVERIFIED** is a discovery/auto-triaged item whose premise references code that already exists \u2014 before scheduling it, verify the premise still holds against the live code; if it is already resolved, deprioritise or cancel it rather than spending a build slot re-verifying shipped work.",
536
+ "",
537
+ ctx.codebaseScan,
538
+ ""
539
+ );
533
540
  }
534
541
  if (ctx.buildPatterns) {
535
542
  parts.push("### Build Patterns", "", ctx.buildPatterns, "");
@@ -1226,7 +1233,7 @@ REFERENCE DOCS
1226
1233
  [Optional \u2014 paths to docs/ files with background context. Omit if not needed.]
1227
1234
 
1228
1235
  FILES LIKELY TOUCHED
1229
- [files]
1236
+ [files \u2014 real paths only. TEST FILE LOCATIONS (task-2606): PAPI tests are NOT co-located next to source in a src/**/__tests__/ folder. Use the actual layout: server tests \u2192 packages/server/tests/<name>.test.ts; adapter-pg tests \u2192 packages/adapter-pg/src/__tests__/<name>.test.ts; dashboard/root tests \u2192 tests/<area>/<name>.test.ts. Do NOT invent packages/server/src/**/__tests__/ paths \u2014 they do not exist.]
1230
1237
 
1231
1238
  EFFORT
1232
1239
  [XS/S/M/L/XL]`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@papi-ai/server",
3
- "version": "0.7.56",
3
+ "version": "0.7.58",
4
4
  "description": "PAPI MCP server — AI-powered sprint planning, build execution, and strategy review for software projects",
5
5
  "license": "Elastic-2.0",
6
6
  "mcpName": "io.github.getpapi/papi",