amsd-pipeline-shared 0.5.38 → 0.5.39

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.
@@ -375,7 +375,8 @@ function setFidelityJudge({ install, env = process.env, logDir }) {
375
375
  return async ({ prompt, schema, codeline }) => {
376
376
  const seamEnv = seamInvocationEnv('prompt-review', path.join(orchOf(install), 'agents'));
377
377
  if (seamEnv.EPAM_ALLOWED_TOOLS) seamEnv.AI_GATE_ALLOW_TOOLS = '1';
378
- seamEnv.EPAM_AGENT_NAME = 'set-fidelity';
378
+ // the call runs AS the prompt-review seam (llm-handler resolves the seam by agent name); the ledger names the check
379
+ seamEnv.EPAM_AGENT_NAME = 'prompt-review';
379
380
  seamEnv.EPAM_RESPONSE_SCHEMA = spec.schemaEnv({ name: 'set_fidelity', description: 'what is geared to the ticket', parameters: schema });
380
381
  const text = await spec.runClaude(promptExec, prompt, path.join(logDir || os.tmpdir(), `set-fidelity-${codeline}.log`), seamEnv, { costAgent: 'set-fidelity' });
381
382
  const m = String(text || '').match(/\{[\s\S]*\}/);
@@ -4470,13 +4470,29 @@ const TOOL_ROSTER_REVIEW = {
4470
4470
  'checked with your tools. Do not answer in prose.',
4471
4471
  parameters: {
4472
4472
  type: 'object',
4473
- required: ['verdict', 'findings'],
4473
+ required: ['verdict', 'findings', 'roles'],
4474
4474
  properties: {
4475
4475
  verdict: {
4476
4476
  type: 'string',
4477
4477
  enum: ['sound', 'defects_found', 'nothing_to_review'],
4478
4478
  description: 'sound = every checkable claim held. defects_found = at least one did not.',
4479
4479
  },
4480
+ // A ROLE OUTLIVES THE TICKET (operator, 2026-10-24): the roster is kept with its codelines and reused for every later story, so
4481
+ // every minted role is answered for, one by one — the rule in prose alone was answered "sound" on a roster whose every agent was
4482
+ // named after the ticket. A role that does not outlive the ticket becomes a blocking finding (rolesOutliveTicketFindings).
4483
+ roles: {
4484
+ type: 'array',
4485
+ description: 'One entry for EVERY minted role reviewed.',
4486
+ items: {
4487
+ type: 'object',
4488
+ required: ['name', 'outlivesTicket', 'why'],
4489
+ properties: {
4490
+ name: { type: 'string', description: 'The minted role, named exactly as in the roster.' },
4491
+ outlivesTicket: { type: 'boolean', description: 'true only when this role — its name and its brief — would serve a story on this codeline about something OTHER than the current ticket: named for the kind of engineer/investigator and the codeline, briefed on the codeline, not on this ticket\'s subject, components or files (except as marked examples).' },
4492
+ why: { type: 'string', description: 'Quote the words that gear the role to the ticket, or say what makes it the codeline\'s.' },
4493
+ },
4494
+ },
4495
+ },
4480
4496
  findings: {
4481
4497
  type: 'array',
4482
4498
  items: {
@@ -4885,6 +4901,19 @@ function _readJsonOrNull(f) {
4885
4901
  * Extracted and exported because it could not be tested otherwise: it sat inside the async function
4886
4902
  * that also does the batching and makes the model calls, so exercising it meant paying for a run.
4887
4903
  */
4904
+ /** A ROLE THAT DOES NOT OUTLIVE THE TICKET IS A BLOCKING FINDING (operator, 2026-10-24): the review answers for every minted role
4905
+ * (TOOL_ROSTER_REVIEW.roles); one it marks outlivesTicket:false — named or briefed for the ticket in scope rather than the codeline —
4906
+ * is turned into the finding the mint's correction loop acts on. The reviewer's own words are the claim. */
4907
+ function rolesOutliveTicketFindings(part) {
4908
+ const roles = part && Array.isArray(part.roles) ? part.roles : [];
4909
+ return roles.filter((r) => r && r.name && r.outlivesTicket === false).map((r) => ({
4910
+ agent: String(r.name), severity: 'blocking', claim: String(r.why || '(the reviewer gave no words)'),
4911
+ checked: 'whether this role, as named and briefed, would serve a story on its codeline about something other than the current ticket',
4912
+ found: 'minted for the ticket in scope, not for the codeline it is kept with',
4913
+ remedy: 'a role named for its kind of work and its codeline, briefed on the codeline — its stack, conventions and how to verify there — naming the current ticket only as a marked example',
4914
+ }));
4915
+ }
4916
+
4888
4917
  function aggregateRosterReview(results, legalVerdicts) {
4889
4918
  const legal = Array.isArray(legalVerdicts) ? legalVerdicts : [];
4890
4919
  const rows = Array.isArray(results) ? results : [];
@@ -4892,7 +4921,9 @@ function aggregateRosterReview(results, legalVerdicts) {
4892
4921
  const unexamined = rows.filter((r) => !r || !r.part || typeof r.part !== 'object'
4893
4922
  || r.part.verdict === 'nothing_to_review' || !r.part.verdict || illegal(r.part.verdict));
4894
4923
  const examined = rows.filter((r) => !unexamined.includes(r));
4895
- const findings = rows.flatMap((r) => (r && r.part && Array.isArray(r.part.findings) ? r.part.findings : []));
4924
+ // a role an examined batch says does not outlive the ticket is a finding of that batch, whatever verdict it gave
4925
+ const roleFindings = examined.flatMap((r) => rolesOutliveTicketFindings(r.part));
4926
+ const findings = rows.flatMap((r) => (r && r.part && Array.isArray(r.part.findings) ? r.part.findings : [])).concat(roleFindings);
4896
4927
 
4897
4928
  if (!examined.length) {
4898
4929
  return {
@@ -4903,7 +4934,7 @@ function aggregateRosterReview(results, legalVerdicts) {
4903
4934
  };
4904
4935
  }
4905
4936
 
4906
- const verdict = examined.some((r) => r.part.verdict === 'defects_found') ? 'defects_found' : 'sound';
4937
+ const verdict = (examined.some((r) => r.part.verdict === 'defects_found') || roleFindings.length) ? 'defects_found' : 'sound';
4907
4938
  return {
4908
4939
  verdict,
4909
4940
  findings,
@@ -11107,7 +11138,7 @@ module.exports = {
11107
11138
  storyAcsBlock,
11108
11139
  specAgentContract,
11109
11140
  rosterReviewVerdict,
11110
- aggregateRosterReview,
11141
+ aggregateRosterReview, rolesOutliveTicketFindings,
11111
11142
  reviewOutcomeKeepsChange,
11112
11143
  // Exported so the brief lookup can be asserted without a run.
11113
11144
  profilesWithProjectBriefs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amsd-pipeline-shared",
3
- "version": "0.5.38",
3
+ "version": "0.5.39",
4
4
  "description": "The AMSD pipeline's shared components, one source for the bash pipeline and its Claude workflows: the model-call gateway (llm-handler.sh — retry, ladder, self-heal, cost) and the libraries it runs.",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {