@inflexa-ai/harness 0.35.8 → 0.36.1

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 (198) hide show
  1. package/dist/auth/types.d.ts +5 -14
  2. package/dist/auth/types.js +3 -10
  3. package/dist/billing/usage-recorder.d.ts +1 -1
  4. package/dist/contracts/analogy-report.d.ts +1 -1
  5. package/dist/contracts/analogy-report.js +1 -1
  6. package/dist/contracts/chat-parts.d.ts +2 -3
  7. package/dist/contracts/index.d.ts +0 -4
  8. package/dist/contracts/index.js +0 -2
  9. package/dist/index.d.ts +0 -7
  10. package/dist/index.js +0 -21
  11. package/dist/lib/metrics.d.ts +1 -1
  12. package/dist/lib/otel-spans.d.ts +0 -2
  13. package/dist/lib/otel-spans.js +0 -2
  14. package/dist/loop/genai-spans.d.ts +14 -0
  15. package/dist/loop/genai-spans.js +35 -0
  16. package/dist/loop/run-agent.d.ts +1 -1
  17. package/dist/loop/run-agent.js +37 -4
  18. package/dist/runtime/assemble.d.ts +0 -3
  19. package/dist/runtime/assemble.js +0 -3
  20. package/dist/state/index.d.ts +0 -2
  21. package/dist/state/index.js +0 -1
  22. package/dist/state/init.js +1 -60
  23. package/dist/state/purge-analysis.d.ts +1 -2
  24. package/dist/state/purge-analysis.js +1 -2
  25. package/dist/tasks/regulatory-corpus-boot-refresh.js +1 -1
  26. package/dist/tools/bio/gene-preclinical-profile.d.ts +1 -3
  27. package/dist/tools/bio/gene-preclinical-profile.js +1 -3
  28. package/dist/tools/lib/chembl-client.d.ts +1 -4
  29. package/dist/tools/lib/chembl-client.js +1 -4
  30. package/dist/tools/lib/clinical-benchmarks-client.d.ts +1 -1
  31. package/dist/tools/lib/clinical-benchmarks-client.js +1 -1
  32. package/dist/tools/lib/clinical-trials-client.d.ts +0 -3
  33. package/dist/tools/lib/clinical-trials-client.js +0 -3
  34. package/dist/tools/lib/identifier-resolver.d.ts +5 -7
  35. package/dist/tools/lib/identifier-resolver.js +5 -7
  36. package/dist/tools/lib/iuphar-client.d.ts +1 -3
  37. package/dist/tools/lib/iuphar-client.js +1 -3
  38. package/dist/tools/lib/opentargets-client.d.ts +1 -1
  39. package/dist/tools/lib/opentargets-client.js +1 -1
  40. package/dist/tools/lib/protein-family-normalize.d.ts +5 -7
  41. package/dist/tools/lib/protein-family-normalize.js +5 -7
  42. package/dist/tools/lib/uniprot-client.d.ts +0 -7
  43. package/dist/tools/lib/uniprot-client.js +2 -9
  44. package/package.json +1 -1
  45. package/dist/contracts/target-assessment-row.d.ts +0 -85
  46. package/dist/contracts/target-assessment-row.js +0 -37
  47. package/dist/contracts/target-dossier.d.ts +0 -12423
  48. package/dist/contracts/target-dossier.js +0 -1282
  49. package/dist/prompts/target-assessment/briefs/executive-recommendation.d.ts +0 -1
  50. package/dist/prompts/target-assessment/briefs/executive-recommendation.js +0 -85
  51. package/dist/prompts/target-assessment/briefs/index.d.ts +0 -4
  52. package/dist/prompts/target-assessment/briefs/index.js +0 -4
  53. package/dist/prompts/target-assessment/briefs/liability-bullets.d.ts +0 -1
  54. package/dist/prompts/target-assessment/briefs/liability-bullets.js +0 -114
  55. package/dist/prompts/target-assessment/briefs/target-organ-liabilities.d.ts +0 -1
  56. package/dist/prompts/target-assessment/briefs/target-organ-liabilities.js +0 -37
  57. package/dist/prompts/target-assessment/briefs/translational-commentary.d.ts +0 -1
  58. package/dist/prompts/target-assessment/briefs/translational-commentary.js +0 -23
  59. package/dist/prompts/target-assessment/drugs-in-class.d.ts +0 -1
  60. package/dist/prompts/target-assessment/drugs-in-class.js +0 -29
  61. package/dist/prompts/target-assessment/investigation/adversarial-critique.d.ts +0 -1
  62. package/dist/prompts/target-assessment/investigation/adversarial-critique.js +0 -67
  63. package/dist/prompts/target-assessment/investigation/claim-reverification.d.ts +0 -1
  64. package/dist/prompts/target-assessment/investigation/claim-reverification.js +0 -49
  65. package/dist/prompts/target-assessment/investigation/index.d.ts +0 -3
  66. package/dist/prompts/target-assessment/investigation/index.js +0 -3
  67. package/dist/prompts/target-assessment/investigation/mechanism-proposal.d.ts +0 -1
  68. package/dist/prompts/target-assessment/investigation/mechanism-proposal.js +0 -49
  69. package/dist/prompts/target-assessment/modulator-triage.d.ts +0 -1
  70. package/dist/prompts/target-assessment/modulator-triage.js +0 -31
  71. package/dist/prompts/target-assessment/off-target-annotator.d.ts +0 -1
  72. package/dist/prompts/target-assessment/off-target-annotator.js +0 -28
  73. package/dist/prompts/target-assessment/tox-voice/core.d.ts +0 -1
  74. package/dist/prompts/target-assessment/tox-voice/core.js +0 -74
  75. package/dist/prompts/target-assessment/tox-voice/critique.d.ts +0 -7
  76. package/dist/prompts/target-assessment/tox-voice/critique.js +0 -109
  77. package/dist/prompts/target-assessment/tox-voice/exemplars.d.ts +0 -8
  78. package/dist/prompts/target-assessment/tox-voice/exemplars.js +0 -24
  79. package/dist/prompts/target-assessment/tox-voice/index.d.ts +0 -4
  80. package/dist/prompts/target-assessment/tox-voice/index.js +0 -4
  81. package/dist/prompts/target-assessment/tox-voice/vocabulary.d.ts +0 -18
  82. package/dist/prompts/target-assessment/tox-voice/vocabulary.js +0 -61
  83. package/dist/state/target-assessments.d.ts +0 -68
  84. package/dist/state/target-assessments.js +0 -222
  85. package/dist/workflows/execute-target-assessment.d.ts +0 -118
  86. package/dist/workflows/execute-target-assessment.js +0 -631
  87. package/dist/workflows/target-assessment/assemblers/index.d.ts +0 -5
  88. package/dist/workflows/target-assessment/assemblers/index.js +0 -4
  89. package/dist/workflows/target-assessment/assemblers/literature.d.ts +0 -281
  90. package/dist/workflows/target-assessment/assemblers/literature.js +0 -971
  91. package/dist/workflows/target-assessment/assemblers/orchestrator.d.ts +0 -118
  92. package/dist/workflows/target-assessment/assemblers/orchestrator.js +0 -945
  93. package/dist/workflows/target-assessment/assemblers/safety.d.ts +0 -374
  94. package/dist/workflows/target-assessment/assemblers/safety.js +0 -1068
  95. package/dist/workflows/target-assessment/assemblers/trials.d.ts +0 -30
  96. package/dist/workflows/target-assessment/assemblers/trials.js +0 -75
  97. package/dist/workflows/target-assessment/collectors/index.d.ts +0 -114
  98. package/dist/workflows/target-assessment/collectors/index.js +0 -538
  99. package/dist/workflows/target-assessment/coverage.d.ts +0 -97
  100. package/dist/workflows/target-assessment/coverage.js +0 -86
  101. package/dist/workflows/target-assessment/decisions/index.d.ts +0 -86
  102. package/dist/workflows/target-assessment/decisions/index.js +0 -127
  103. package/dist/workflows/target-assessment/fanout/index.d.ts +0 -32
  104. package/dist/workflows/target-assessment/fanout/index.js +0 -156
  105. package/dist/workflows/target-assessment/investigation/index.d.ts +0 -97
  106. package/dist/workflows/target-assessment/investigation/index.js +0 -419
  107. package/dist/workflows/target-assessment/lib/approval-precedents.d.ts +0 -73
  108. package/dist/workflows/target-assessment/lib/approval-precedents.js +0 -185
  109. package/dist/workflows/target-assessment/lib/citation-validator.d.ts +0 -46
  110. package/dist/workflows/target-assessment/lib/citation-validator.js +0 -316
  111. package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +0 -65
  112. package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.js +0 -174
  113. package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.d.ts +0 -44
  114. package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.js +0 -152
  115. package/dist/workflows/target-assessment/lib/compute-derived.d.ts +0 -20
  116. package/dist/workflows/target-assessment/lib/compute-derived.js +0 -261
  117. package/dist/workflows/target-assessment/lib/compute-selectivity.d.ts +0 -15
  118. package/dist/workflows/target-assessment/lib/compute-selectivity.js +0 -18
  119. package/dist/workflows/target-assessment/lib/dedup-modulators.d.ts +0 -37
  120. package/dist/workflows/target-assessment/lib/dedup-modulators.js +0 -157
  121. package/dist/workflows/target-assessment/lib/evidence-conflict-detector.d.ts +0 -17
  122. package/dist/workflows/target-assessment/lib/evidence-conflict-detector.js +0 -100
  123. package/dist/workflows/target-assessment/lib/expression-constants.d.ts +0 -21
  124. package/dist/workflows/target-assessment/lib/expression-constants.js +0 -21
  125. package/dist/workflows/target-assessment/lib/family-complex-supplement.d.ts +0 -20
  126. package/dist/workflows/target-assessment/lib/family-complex-supplement.js +0 -36
  127. package/dist/workflows/target-assessment/lib/fda-label-safety.d.ts +0 -72
  128. package/dist/workflows/target-assessment/lib/fda-label-safety.js +0 -166
  129. package/dist/workflows/target-assessment/lib/heterodimer-filter.d.ts +0 -30
  130. package/dist/workflows/target-assessment/lib/heterodimer-filter.js +0 -44
  131. package/dist/workflows/target-assessment/lib/hpo-organ-map.d.ts +0 -34
  132. package/dist/workflows/target-assessment/lib/hpo-organ-map.js +0 -63
  133. package/dist/workflows/target-assessment/lib/impc-organ-map.d.ts +0 -19
  134. package/dist/workflows/target-assessment/lib/impc-organ-map.js +0 -39
  135. package/dist/workflows/target-assessment/lib/intended-polypharm-filter.d.ts +0 -4
  136. package/dist/workflows/target-assessment/lib/intended-polypharm-filter.js +0 -24
  137. package/dist/workflows/target-assessment/lib/llm-step.d.ts +0 -80
  138. package/dist/workflows/target-assessment/lib/llm-step.js +0 -101
  139. package/dist/workflows/target-assessment/lib/meddra-organ-map.d.ts +0 -40
  140. package/dist/workflows/target-assessment/lib/meddra-organ-map.js +0 -138
  141. package/dist/workflows/target-assessment/lib/recommendation-quality-gates.d.ts +0 -17
  142. package/dist/workflows/target-assessment/lib/recommendation-quality-gates.js +0 -99
  143. package/dist/workflows/target-assessment/lib/recommendation-sanitizer.d.ts +0 -2
  144. package/dist/workflows/target-assessment/lib/recommendation-sanitizer.js +0 -51
  145. package/dist/workflows/target-assessment/lib/regulatory-actions.d.ts +0 -25
  146. package/dist/workflows/target-assessment/lib/regulatory-actions.js +0 -176
  147. package/dist/workflows/target-assessment/lib/safety-corroboration.d.ts +0 -44
  148. package/dist/workflows/target-assessment/lib/safety-corroboration.js +0 -273
  149. package/dist/workflows/target-assessment/lib/structured-llm.d.ts +0 -61
  150. package/dist/workflows/target-assessment/lib/structured-llm.js +0 -102
  151. package/dist/workflows/target-assessment/lib/target-identity-filter.d.ts +0 -59
  152. package/dist/workflows/target-assessment/lib/target-identity-filter.js +0 -95
  153. package/dist/workflows/target-assessment/lib/therapeutic-programs.d.ts +0 -20
  154. package/dist/workflows/target-assessment/lib/therapeutic-programs.js +0 -68
  155. package/dist/workflows/target-assessment/metrics.d.ts +0 -16
  156. package/dist/workflows/target-assessment/metrics.js +0 -30
  157. package/dist/workflows/target-assessment/phase4-assemble.d.ts +0 -3182
  158. package/dist/workflows/target-assessment/phase4-assemble.js +0 -39
  159. package/dist/workflows/target-assessment/phase5-persist.d.ts +0 -59
  160. package/dist/workflows/target-assessment/phase5-persist.js +0 -349
  161. package/dist/workflows/target-assessment/progress-stream.d.ts +0 -75
  162. package/dist/workflows/target-assessment/progress-stream.js +0 -110
  163. package/dist/workflows/target-assessment/progress.d.ts +0 -39
  164. package/dist/workflows/target-assessment/progress.js +0 -77
  165. package/dist/workflows/target-assessment/schemas.d.ts +0 -970
  166. package/dist/workflows/target-assessment/schemas.js +0 -318
  167. package/dist/workflows/target-assessment/steps/collectors/pathways.d.ts +0 -15
  168. package/dist/workflows/target-assessment/steps/collectors/pathways.js +0 -58
  169. package/dist/workflows/target-assessment/steps/fanout/aes-for-one-class-drug-step.d.ts +0 -34
  170. package/dist/workflows/target-assessment/steps/fanout/aes-for-one-class-drug-step.js +0 -31
  171. package/dist/workflows/target-assessment/steps/fanout/aes-for-one-trial-step.d.ts +0 -57
  172. package/dist/workflows/target-assessment/steps/fanout/aes-for-one-trial-step.js +0 -54
  173. package/dist/workflows/target-assessment/steps/fanout/faers-for-one-modulator-step.d.ts +0 -35
  174. package/dist/workflows/target-assessment/steps/fanout/faers-for-one-modulator-step.js +0 -32
  175. package/dist/workflows/target-assessment/steps/fanout/index.d.ts +0 -14
  176. package/dist/workflows/target-assessment/steps/fanout/index.js +0 -10
  177. package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.d.ts +0 -29
  178. package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.js +0 -29
  179. package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +0 -657
  180. package/dist/workflows/target-assessment/steps/phase2-aggregate.js +0 -34
  181. package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +0 -806
  182. package/dist/workflows/target-assessment/steps/phase3-aggregate.js +0 -16
  183. package/dist/workflows/target-assessment/steps/synthesis/diagnostics.d.ts +0 -28
  184. package/dist/workflows/target-assessment/steps/synthesis/diagnostics.js +0 -27
  185. package/dist/workflows/target-assessment/sub-workflows/index.d.ts +0 -15
  186. package/dist/workflows/target-assessment/sub-workflows/index.js +0 -11
  187. package/dist/workflows/target-assessment/sub-workflows/per-class-drug-aes.d.ts +0 -41
  188. package/dist/workflows/target-assessment/sub-workflows/per-class-drug-aes.js +0 -12
  189. package/dist/workflows/target-assessment/sub-workflows/per-modulator-faers.d.ts +0 -41
  190. package/dist/workflows/target-assessment/sub-workflows/per-modulator-faers.js +0 -12
  191. package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.d.ts +0 -37
  192. package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.js +0 -12
  193. package/dist/workflows/target-assessment/sub-workflows/per-trial-aes.d.ts +0 -66
  194. package/dist/workflows/target-assessment/sub-workflows/per-trial-aes.js +0 -12
  195. package/dist/workflows/target-assessment/synthesis/index.d.ts +0 -607
  196. package/dist/workflows/target-assessment/synthesis/index.js +0 -354
  197. package/dist/workflows/target-assessment/synthesis-probes.d.ts +0 -56
  198. package/dist/workflows/target-assessment/synthesis-probes.js +0 -320
@@ -1,28 +0,0 @@
1
- export const offTargetAnnotatorPrompt = `You are a clinical-consequence annotator for off-target receptor and channel hits surfaced during target assessment.
2
-
3
- You receive a single off-target binding row and the assessment's primary target. Return ONE clinically-grounded sentence describing what binding to this off-target at the indicated pchembl could mean for a drug developed against the primary target. Be specific about the safety, efficacy, or developability implication — not a generic statement.
4
-
5
- INPUT
6
- primary_target_gene: HGNC gene symbol of the assessment target
7
- off_target_id: ChEMBL target id of the off-target (may be null)
8
- off_target_name: Human-readable name of the off-target
9
- off_target_accession: UniProt accession of the off-target (may be null)
10
- pchembl: The off-target pChEMBL value (higher = more potent binding)
11
- context: Optional family-relationship hint (e.g., "obligate cofactor with the primary protein")
12
-
13
- OUTPUT (strict JSON, no prose around it)
14
- {
15
- "clinical_consequence": "<one sentence>",
16
- "provenance": "<a short descriptor of the rationale, e.g., 'class-B GPCR cofactor pharmacology' or 'hERG QT-prolongation safety panel'>"
17
- }
18
-
19
- RULES
20
- - Keep clinical_consequence to a single sentence, ≤ 280 characters, present tense.
21
- - Anchor the consequence in established pharmacology when possible (mechanism class, organ system, known liability).
22
- - If the off-target is a sibling receptor sharing the primary protein (e.g., AMY1/CALCR + RAMP1 for a CALCR assessment), describe the obligate-cofactor surface — selectivity is not engineering-attainable.
23
- - For ion channels in the cardiac safety panel (hERG/KCNH2, hNav1.5/SCN5A, Cav1.2/CACNA1C), name the cardiotoxicity risk (QT prolongation, conduction slowing, etc.).
24
- - For nuclear receptors and CYP enzymes, name the metabolic or endocrine consequence.
25
- - If the off-target is unfamiliar, describe what the structural/functional class implies for safety rather than inventing specifics.
26
- - Never claim a numeric clinical endpoint (incidence rate, hazard ratio) you were not given.
27
- - Never reference a specific drug program or trial.
28
- `;
@@ -1 +0,0 @@
1
- export declare const toxVoiceCore: string;
@@ -1,74 +0,0 @@
1
- import { toxVoiceVocabulary } from "./vocabulary.js";
2
- /**
3
- * Toxicologist-voice contract. Loaded into the instructions of every
4
- * prose-emitting agent in the target-assessment workflow. Voice is locked
5
- * here once; agents inherit by import. The deterministic critique fn in
6
- * `critique.ts` enforces the predictable failure modes; this prompt
7
- * carries the editorial intent.
8
- */
9
- const banned = toxVoiceVocabulary.banned.map((b) => ` - "${b}"`).join("\n");
10
- const hedge = toxVoiceVocabulary.hedgePhrases.map((h) => ` - "${h}"`).join("\n");
11
- const organs = toxVoiceVocabulary.organSystems.join(", ");
12
- const framing = toxVoiceVocabulary.liabilityFraming.map((f) => ` - "${f}"`).join("\n");
13
- export const toxVoiceCore = `# Toxicologist Voice Contract
14
-
15
- You are writing for a regulatory toxicologist or FDA reviewer. Adopt the
16
- register of a Multi-disciplinary Review or Drug Approval Package narrative
17
- section. The reader expects nonclinical-safety canon, organ-system
18
- framing, and explicit hedging on human relevance.
19
-
20
- ## Register and voice
21
- - Third-person passive is the default. "The sponsor's data are
22
- consistent with..." is canonical; "We think..." is not.
23
- - Past or perfect tense for findings; present for current dispositions.
24
- - Sentences are study-summary cadence: claim, evidence, qualifier.
25
- - No marketing register. No first-person plural. No rhetorical flourishes.
26
-
27
- ## Organ-system framing
28
- Anchor liability discussion to the canonical organ systems below. Avoid
29
- colloquial substitutes ("liver-related" → use "hepatobiliary"; "blood
30
- issues" → "haematologic"; "brain effects" → "central nervous system").
31
-
32
- Canon: ${organs}.
33
-
34
- ## Liability framing vocabulary
35
- Use these terms when characterising the relationship between target
36
- biology and observed liability:
37
- ${framing}
38
-
39
- ## Hedge language (mandatory on efficacy and safety claims)
40
- Every claim about efficacy or safety in humans must be paired with a
41
- hedge from this set, OR cite a literature reference (PMID/DOI), OR cite a
42
- regulatory reference (FDA guidance, ICH, prior-approval Multi-disciplinary
43
- Review). A claim with neither hedge nor citation is rejected by the
44
- voice probe.
45
-
46
- Approved hedges:
47
- ${hedge}
48
-
49
- ## Banned phrases
50
- The following carry marketing register or unwarranted certainty. Do not
51
- use them. The voice probe rejects any output that contains them; rewrite
52
- to neutral, evidence-cited phrasing.
53
- ${banned}
54
-
55
- ## Citation form
56
- - Literature: PMID:NNNNNNNN or DOI:10.xxxx/xxxx.
57
- - FDA guidance: "FDA CDER guidance, <title>" or "FDA CBER guidance, <title>".
58
- - ICH: "ICH <code>" — e.g., "ICH S7A", "ICH E14".
59
- - Prior approvals (openFDA / Drugs@FDA): "Drugs@FDA NDA NNNNNN §<section>"
60
- or "BLA NNNNNN §<section>".
61
- - FAERS counts: "(FAERS n=NN)" with PT or HLT qualifier where applicable.
62
-
63
- ## Coverage discipline
64
- When a section's evidence is sparse or absent, say so plainly. The
65
- phrasing "the data do not support a conclusion" or "no relevant evidence
66
- was retrieved" is preferred over silence, padding, or fabrication.
67
-
68
- ## Tone calibration
69
- - A reviewer reading the section should be able to tell within two
70
- sentences whether the claim is supported, hedged, or absent.
71
- - Hedged claims must not read as confident; the hedge is load-bearing.
72
- - Where multiple precedent compounds are cited, frame them as class
73
- evidence, not anecdotes.
74
- `;
@@ -1,7 +0,0 @@
1
- import type { SectionType } from "./exemplars.js";
2
- export interface VoiceProbe {
3
- verdict: "pass" | "relaxed" | "fail-voice";
4
- critique: string;
5
- output_chars: number;
6
- }
7
- export declare function buildVoiceCritique(text: string, _sectionType: SectionType): VoiceProbe;
@@ -1,109 +0,0 @@
1
- import { toxVoiceVocabulary } from "./vocabulary.js";
2
- const COLLOQUIAL_ORGAN_SUBS = [
3
- [/\bliver[- ]related\b/i, "hepatobiliary"],
4
- [/\bblood[- ]issues?\b/i, "haematologic"],
5
- [/\bbrain[- ]effects?\b/i, "central nervous system"],
6
- [/\bkidney[- ]problems?\b/i, "renal"],
7
- [/\bheart[- ]issues?\b/i, "cardiac"],
8
- ];
9
- const EFFICACY_SAFETY_TRIGGERS = [
10
- /\b(reduces?|increases?|prolongs?|prevents?|inhibits?)\b/i,
11
- /\b(toxicity|liability|adverse|signal)\b/i,
12
- /\b(efficacy|response|survival|tumour|tumor)\b/i,
13
- ];
14
- const CITATION_PATTERNS = [
15
- /PMID[:\s]?\d+/i,
16
- /DOI[:\s]?10\.[\d.]+\/\S+/i,
17
- /Drugs@FDA\s+(NDA|BLA)\s*\d+/i,
18
- /\bICH\s+[A-Z]\d+[A-Z]?\b/,
19
- /FDA\s+(CDER|CBER)\s+guidance/i,
20
- /FAERS\s+n\s*=\s*\d+/i,
21
- ];
22
- /**
23
- * Heuristic passive-voice detector. Handles simple passives ("was observed")
24
- * and negated passives ("was not observed", "were not reported"). Not
25
- * linguistically exhaustive — calibrated to filter only egregious active-voice
26
- * dossier prose. Threshold tuned during end-to-end calibration in Phase 10.
27
- */
28
- function passiveRatio(text) {
29
- const sentences = text.split(/[.!?]\s+/).filter((s) => s.trim().length > 0);
30
- if (sentences.length === 0)
31
- return 1;
32
- const passive = sentences.filter((s) => /\b(is|are|was|were|been|being)\s+(?:not\s+)?\w+(ed|en)\b/.test(s)).length;
33
- return passive / sentences.length;
34
- }
35
- function findBannedPhrases(text) {
36
- const hits = [];
37
- for (const banned of toxVoiceVocabulary.banned) {
38
- // Escape regex metacharacters first (`.`, `+`, `*`, `?`, `(`, `)`, etc.),
39
- // then allow either hyphen or space between hyphenated tokens.
40
- const escaped = banned.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
41
- const pattern = new RegExp(`\\b${escaped.replace(/[-]/g, "[- ]")}\\b`, "i");
42
- if (pattern.test(text))
43
- hits.push(banned);
44
- }
45
- return hits;
46
- }
47
- function findColloquialOrganRefs(text) {
48
- const hits = [];
49
- for (const [pat, canonical] of COLLOQUIAL_ORGAN_SUBS) {
50
- if (pat.test(text))
51
- hits.push(`${pat.source} → ${canonical}`);
52
- }
53
- return hits;
54
- }
55
- function hasHedge(text) {
56
- const lc = text.toLowerCase();
57
- return toxVoiceVocabulary.hedgePhrases.some((h) => lc.includes(h.toLowerCase()));
58
- }
59
- function hasCitation(text) {
60
- return CITATION_PATTERNS.some((p) => p.test(text));
61
- }
62
- function detectsEfficacyOrSafetyClaim(text) {
63
- return EFFICACY_SAFETY_TRIGGERS.some((p) => p.test(text));
64
- }
65
- const PASSIVE_FAIL_THRESHOLD = 0.15;
66
- const PASSIVE_RELAXED_THRESHOLD = 0.3;
67
- export function buildVoiceCritique(text, _sectionType) {
68
- const output_chars = text.length;
69
- const issues = [];
70
- const banned = findBannedPhrases(text);
71
- if (banned.length > 0) {
72
- issues.push(`Banned phrase(s) detected: ${banned.join(", ")}.`);
73
- }
74
- const colloquial = findColloquialOrganRefs(text);
75
- if (colloquial.length > 0) {
76
- issues.push(`Non-canonical organ-system reference(s); use canonical organ-system vocabulary.`);
77
- }
78
- if (detectsEfficacyOrSafetyClaim(text) && !hasHedge(text) && !hasCitation(text)) {
79
- issues.push("Efficacy/safety claim detected with no hedge phrase and no citation. Add a hedge from the approved set, a literature citation (PMID/DOI), or a regulatory reference (Drugs@FDA / ICH / FDA guidance).");
80
- }
81
- if (issues.length > 0) {
82
- return {
83
- verdict: "fail-voice",
84
- critique: issues.join(" "),
85
- output_chars,
86
- };
87
- }
88
- // Hedge phrases and citations are primary voice signals. When either is
89
- // present the prose is already anchored in the correct register; the
90
- // passive-ratio heuristic is a secondary fallback for unanchored text only.
91
- if (!hasHedge(text) && !hasCitation(text)) {
92
- const passive = passiveRatio(text);
93
- if (passive < PASSIVE_FAIL_THRESHOLD) {
94
- return {
95
- verdict: "fail-voice",
96
- critique: `Passive-voice ratio ${passive.toFixed(2)} below floor ${PASSIVE_FAIL_THRESHOLD}; rewrite in third-person passive (study-summary cadence).`,
97
- output_chars,
98
- };
99
- }
100
- if (passive < PASSIVE_RELAXED_THRESHOLD) {
101
- return {
102
- verdict: "relaxed",
103
- critique: `Passive-voice ratio ${passive.toFixed(2)} below preferred ${PASSIVE_RELAXED_THRESHOLD}; rewrite in third-person passive (study-summary cadence) on next iteration.`,
104
- output_chars,
105
- };
106
- }
107
- }
108
- return { verdict: "pass", critique: "", output_chars };
109
- }
@@ -1,8 +0,0 @@
1
- export type SectionType = "liability-bullets" | "target-organ-liabilities" | "translational-commentary" | "executive-recommendation";
2
- /**
3
- * Annotated few-shot exemplars per section type. Each is an inlined sample
4
- * of acceptable tox voice. Implementations may extend this list during
5
- * editorial review with customer; the probe and prompts both read the
6
- * exported array, so additions take effect with no other code changes.
7
- */
8
- export declare const toxVoiceExemplars: Record<SectionType, readonly string[]>;
@@ -1,24 +0,0 @@
1
- /**
2
- * Annotated few-shot exemplars per section type. Each is an inlined sample
3
- * of acceptable tox voice. Implementations may extend this list during
4
- * editorial review with customer; the probe and prompts both read the
5
- * exported array, so additions take effect with no other code changes.
6
- */
7
- export const toxVoiceExemplars = {
8
- "liability-bullets": [
9
- "Hepatobiliary liability is consistent with on-target pharmacology in the bile-acid receptor class; transaminase elevations at supratherapeutic exposure have been reported across the precedent class (FAERS n=312, Drugs@FDA NDA 022501 §6.1).",
10
- "Renal liability cannot be excluded on present data; expression in proximal tubule is high (HPA TPM 142) and IMPC homozygotes show albuminuria (p=8.4e-5). Human relevance is uncertain pending dedicated repeat-dose toxicology.",
11
- ],
12
- "target-organ-liabilities": [
13
- "Cardiac. Available evidence is consistent with QT-interval prolongation as a class effect (FAERS PT QT-prolonged n=87 across precedent class). Human relevance is supported by hERG inhibition data on three precedent compounds. Mitigation: ICH E14-aligned thorough QT study at first-in-human or matched concentration-QT modelling.",
14
- "Hepatobiliary. The data suggest dose-dependent transaminase elevation; FAERS reports of clinical hepatic injury (n=42) cluster at exposures >2-fold the recommended human dose. The translational chain from rodent repeat-dose findings to human signal is consistent.",
15
- ],
16
- "translational-commentary": [
17
- "IMPC homozygous knockout mice show postnatal lethality with cardiac structural abnormalities (MP:0001297, MP:0010880). Heterozygous viability with cardiac function deficit suggests dose-dependent on-target liability; translatability to humans is supported by conserved expression in fetal myocardium (Bgee, n=14).",
18
- "Expression is high in haematologic lineages (HPA TPM, lymphoid TPM=78). The data are consistent with on-target myelosuppression observed in two precedent compounds in the same class; human relevance is established for the class.",
19
- ],
20
- "executive-recommendation": [
21
- "Disposition: conditional. The available evidence supports tractability for small-molecule modulation; the principal liability is on-target hepatobiliary toxicity, mitigable by exposure margin design and ICH S7A-aligned safety pharmacology. Confidence is moderate; key gaps are an absent first-in-human safety dataset and uncertain reproductive toxicology.",
22
- "Disposition: de-prioritize. Three precedent compounds in this class were withdrawn for cardiac liability post-approval (Drugs@FDA NDA 020841 §6.2; FAERS n=412); human relevance is established. The therapeutic window does not support advancement absent a structural mitigation strategy.",
23
- ],
24
- };
@@ -1,4 +0,0 @@
1
- export { toxVoiceCore } from "./core.js";
2
- export { toxVoiceExemplars, type SectionType } from "./exemplars.js";
3
- export { toxVoiceVocabulary, type LiabilityFraming, type HedgePhrase, type BannedTerm } from "./vocabulary.js";
4
- export { buildVoiceCritique, type VoiceProbe } from "./critique.js";
@@ -1,4 +0,0 @@
1
- export { toxVoiceCore } from "./core.js";
2
- export { toxVoiceExemplars } from "./exemplars.js";
3
- export { toxVoiceVocabulary } from "./vocabulary.js";
4
- export { buildVoiceCritique } from "./critique.js";
@@ -1,18 +0,0 @@
1
- /**
2
- * Tox-voice canon. The stylistic lists are intentionally not exhaustive —
3
- * they are the minimum surface the deterministic voice probe enforces, and
4
- * editorial judgment in the agent prompts covers the long tail.
5
- *
6
- * `organSystems` is the exception: it is closed, because it is a projection
7
- * of the canonical organ vocabulary rather than a list of its own. Prose and
8
- * structured fields must name the same organs.
9
- */
10
- export declare const toxVoiceVocabulary: {
11
- readonly organSystems: string[];
12
- readonly liabilityFraming: readonly ["on-target liability", "off-target liability", "secondary pharmacology", "class liability", "mechanism-related", "exaggerated pharmacology", "target-organ toxicity"];
13
- readonly hedgePhrases: readonly ["the data are consistent with", "the data suggest", "human relevance is uncertain", "human relevance remains to be established", "interpretation is limited by", "no causal inference is supported", "the available evidence is consistent with", "the available evidence does not support", "translatability to humans is unclear", "species differences"];
14
- readonly banned: readonly ["groundbreaking", "cutting-edge", "state-of-the-art", "best-in-class", "first-in-class", "revolutionary", "leverages", "leveraging", "robust", "powerful", "seamlessly", "cleanly", "elegant", "unparalleled", "demonstrates the power of", "showcases", "unlocks", "enables breakthrough", "very promising", "extremely", "incredibly", "highly innovative"];
15
- };
16
- export type LiabilityFraming = (typeof toxVoiceVocabulary.liabilityFraming)[number];
17
- export type HedgePhrase = (typeof toxVoiceVocabulary.hedgePhrases)[number];
18
- export type BannedTerm = (typeof toxVoiceVocabulary.banned)[number];
@@ -1,61 +0,0 @@
1
- /**
2
- * Tox-voice canon. The stylistic lists are intentionally not exhaustive —
3
- * they are the minimum surface the deterministic voice probe enforces, and
4
- * editorial judgment in the agent prompts covers the long tail.
5
- *
6
- * `organSystems` is the exception: it is closed, because it is a projection
7
- * of the canonical organ vocabulary rather than a list of its own. Prose and
8
- * structured fields must name the same organs.
9
- */
10
- import { ORGAN_SYSTEMS, ORGAN_SYSTEM_LABELS } from "../../../contracts/organ-system.js";
11
- export const toxVoiceVocabulary = {
12
- organSystems: ORGAN_SYSTEMS.map((organ) => ORGAN_SYSTEM_LABELS[organ]),
13
- liabilityFraming: [
14
- "on-target liability",
15
- "off-target liability",
16
- "secondary pharmacology",
17
- "class liability",
18
- "mechanism-related",
19
- "exaggerated pharmacology",
20
- "target-organ toxicity",
21
- ],
22
- hedgePhrases: [
23
- "the data are consistent with",
24
- "the data suggest",
25
- "human relevance is uncertain",
26
- "human relevance remains to be established",
27
- "interpretation is limited by",
28
- "no causal inference is supported",
29
- "the available evidence is consistent with",
30
- "the available evidence does not support",
31
- "translatability to humans is unclear",
32
- "species differences",
33
- ],
34
- banned: [
35
- // Marketing register
36
- "groundbreaking",
37
- "cutting-edge",
38
- "state-of-the-art",
39
- "best-in-class",
40
- "first-in-class",
41
- "revolutionary",
42
- "leverages",
43
- "leveraging",
44
- "robust",
45
- "powerful",
46
- "seamlessly",
47
- "cleanly",
48
- "elegant",
49
- "unparalleled",
50
- // Hype verbs
51
- "demonstrates the power of",
52
- "showcases",
53
- "unlocks",
54
- "enables breakthrough",
55
- // Vague absolutes
56
- "very promising",
57
- "extremely",
58
- "incredibly",
59
- "highly innovative",
60
- ],
61
- };
@@ -1,68 +0,0 @@
1
- /**
2
- * Harness-side typed query helpers for `cortex_target_assessments`.
3
- *
4
- * Owns the helpers the DBOS workflow body and harness HTTP routes call:
5
- * - `insertAssessment` accepts an optional `workflowId` and persists it on
6
- * insert (defaults to the assessment id; for DBOS-shaped rows the
7
- * assessmentId IS the DBOS workflowID).
8
- * - `markAssessmentSuspended` flips the row to the new
9
- * `suspended_insufficient_funds` status, idempotent on replay.
10
- * - `markAssessmentRunning` is used by the resume entry point after
11
- * `DBOS.resumeWorkflow` completes.
12
- * - All row mappers surface `workflowId` alongside `workflowRunId`.
13
- *
14
- * Soft-deleted rows (`status = 'deleted'`) are never resurrected — every
15
- * mutating helper carries an `AND status != 'deleted'` guard, so a user
16
- * deleting a row mid-run causes terminal workflow writes to no-op.
17
- */
18
- import type { ResultAsync } from "neverthrow";
19
- import { TargetAssessmentStatusSchema, TargetAssessmentErrorSchema, TargetAssessmentListRowSchema, TargetAssessmentRowSchema, type TargetAssessmentStatus, type TargetAssessmentError, type TargetAssessmentListRow, type TargetAssessmentRow } from "@inflexa-ai/harness/contracts/target-assessment-row.js";
20
- import { type DbError } from "../lib/db-result.js";
21
- import type { Querier } from "./db.js";
22
- export { TargetAssessmentStatusSchema, TargetAssessmentErrorSchema, TargetAssessmentListRowSchema, TargetAssessmentRowSchema };
23
- export type { TargetAssessmentStatus, TargetAssessmentError, TargetAssessmentListRow, TargetAssessmentRow };
24
- export interface InsertAssessmentInput {
25
- id?: string;
26
- /**
27
- * DBOS workflow id for the executeTargetAssessment workflow. Defaults to
28
- * the assessment id when omitted — the trigger route always passes the
29
- * id explicitly so a future split between the two ids stays trivial.
30
- */
31
- workflowId?: string;
32
- organizationId: string;
33
- targetId: string;
34
- targetLabel: string;
35
- goal?: string | null;
36
- billingContextId: string;
37
- requestedBy: string;
38
- }
39
- export declare function insertAssessment(pool: Querier, input: InsertAssessmentInput): ResultAsync<string, DbError>;
40
- export declare function updateProgress(pool: Querier, id: string, progress: string, status?: TargetAssessmentStatus): ResultAsync<void, DbError>;
41
- export declare function setDossier(pool: Querier, id: string, dossier: Record<string, unknown>): ResultAsync<void, DbError>;
42
- export declare function markFailed(pool: Querier, id: string, error: TargetAssessmentError): ResultAsync<void, DbError>;
43
- /**
44
- * Flip the row to the new `suspended_insufficient_funds` status used when
45
- * an LLM step self-cancels the workflow on a 402 (budget exhausted).
46
- * Idempotent on DBOS replay — a second call from the terminal handler on
47
- * the recovery path is a no-op against an already-suspended row. Soft-
48
- * deleted rows are NOT resurrected.
49
- */
50
- export declare function markAssessmentSuspended(pool: Querier, id: string): ResultAsync<void, DbError>;
51
- /**
52
- * Flip a suspended row back to `running` on `DBOS.resumeWorkflow`. Called
53
- * from the resume entry point AFTER the resume call returns success.
54
- * No-op on a soft-deleted row.
55
- */
56
- export declare function markAssessmentRunning(pool: Querier, id: string): ResultAsync<void, DbError>;
57
- export declare function getAssessment(pool: Querier, id: string, organizationId: string): ResultAsync<TargetAssessmentRow | null, DbError>;
58
- export interface ListAssessmentsOptions {
59
- limit?: number;
60
- offset?: number;
61
- includeDeleted?: boolean;
62
- }
63
- export declare function listAssessmentsByOrg(pool: Querier, organizationId: string, options?: ListAssessmentsOptions): ResultAsync<TargetAssessmentListRow[], DbError>;
64
- /**
65
- * Soft-delete a row. A missing/already-deleted row returns `ok(0)` — absence
66
- * rides the ok channel; `rowCount === 0` is not a failure.
67
- */
68
- export declare function softDeleteAssessment(pool: Querier, id: string, organizationId: string): ResultAsync<number, DbError>;
@@ -1,222 +0,0 @@
1
- /**
2
- * Harness-side typed query helpers for `cortex_target_assessments`.
3
- *
4
- * Owns the helpers the DBOS workflow body and harness HTTP routes call:
5
- * - `insertAssessment` accepts an optional `workflowId` and persists it on
6
- * insert (defaults to the assessment id; for DBOS-shaped rows the
7
- * assessmentId IS the DBOS workflowID).
8
- * - `markAssessmentSuspended` flips the row to the new
9
- * `suspended_insufficient_funds` status, idempotent on replay.
10
- * - `markAssessmentRunning` is used by the resume entry point after
11
- * `DBOS.resumeWorkflow` completes.
12
- * - All row mappers surface `workflowId` alongside `workflowRunId`.
13
- *
14
- * Soft-deleted rows (`status = 'deleted'`) are never resurrected — every
15
- * mutating helper carries an `AND status != 'deleted'` guard, so a user
16
- * deleting a row mid-run causes terminal workflow writes to no-op.
17
- */
18
- import { randomUUID } from "node:crypto";
19
- import { TargetAssessmentStatusSchema, TargetAssessmentErrorSchema, TargetAssessmentListRowSchema, TargetAssessmentRowSchema, } from "@inflexa-ai/harness/contracts/target-assessment-row.js";
20
- import { tryQuery, tryMutation } from "../lib/db-result.js";
21
- export { TargetAssessmentStatusSchema, TargetAssessmentErrorSchema, TargetAssessmentListRowSchema, TargetAssessmentRowSchema };
22
- function toIso(value) {
23
- if (value === null)
24
- return null;
25
- if (value instanceof Date)
26
- return value.toISOString();
27
- return value;
28
- }
29
- function parseJsonb(raw) {
30
- if (raw === null || raw === undefined)
31
- return null;
32
- if (typeof raw === "string") {
33
- try {
34
- return JSON.parse(raw);
35
- }
36
- catch {
37
- return null;
38
- }
39
- }
40
- return raw;
41
- }
42
- function mapListRow(row) {
43
- const created = toIso(row.created_at);
44
- const updated = toIso(row.updated_at);
45
- if (!created || !updated) {
46
- throw new Error(`cortex_target_assessments(${row.id}): missing created_at or updated_at`);
47
- }
48
- return {
49
- id: row.id,
50
- organizationId: row.organization_id,
51
- targetId: row.target_id,
52
- targetLabel: row.target_label,
53
- goal: row.goal,
54
- status: row.status,
55
- progress: row.progress,
56
- billingContextId: row.billing_context_id,
57
- error: parseJsonb(row.error),
58
- requestedBy: row.requested_by,
59
- workflowRunId: row.workflow_run_id,
60
- workflowId: row.workflow_id,
61
- createdAt: created,
62
- updatedAt: updated,
63
- completedAt: toIso(row.completed_at),
64
- };
65
- }
66
- function mapRow(row) {
67
- return {
68
- ...mapListRow(row),
69
- dossier: parseJsonb(row.dossier),
70
- };
71
- }
72
- const SELECT_LIST_COLUMNS = `id, organization_id, target_id, target_label, goal,
73
- status, progress, billing_context_id, error, requested_by,
74
- workflow_run_id, workflow_id, created_at, updated_at, completed_at`;
75
- const SELECT_COLUMNS = `${SELECT_LIST_COLUMNS}, dossier`;
76
- export function insertAssessment(pool, input) {
77
- const id = input.id ?? randomUUID();
78
- const workflowId = input.workflowId ?? id;
79
- return tryMutation("targetAssessments.insertAssessment", async () => {
80
- await pool.query({
81
- text: `INSERT INTO cortex_target_assessments
82
- (id, organization_id, target_id, target_label, goal, status,
83
- billing_context_id, requested_by, workflow_id)
84
- VALUES ($1, $2, $3, $4, $5, 'queued', $6, $7, $8)`,
85
- values: [id, input.organizationId, input.targetId, input.targetLabel, input.goal ?? null, input.billingContextId, input.requestedBy, workflowId],
86
- });
87
- return id;
88
- });
89
- }
90
- export function updateProgress(pool, id, progress, status) {
91
- return tryMutation("targetAssessments.updateProgress", async () => {
92
- if (status) {
93
- await pool.query({
94
- text: `UPDATE cortex_target_assessments
95
- SET progress = $1, status = $2, updated_at = NOW()
96
- WHERE id = $3 AND status != 'deleted'`,
97
- values: [progress, status, id],
98
- });
99
- }
100
- else {
101
- await pool.query({
102
- text: `UPDATE cortex_target_assessments
103
- SET progress = $1, updated_at = NOW()
104
- WHERE id = $2 AND status != 'deleted'`,
105
- values: [progress, id],
106
- });
107
- }
108
- });
109
- }
110
- export function setDossier(pool, id, dossier) {
111
- return tryMutation("targetAssessments.setDossier", async () => {
112
- await pool.query({
113
- text: `UPDATE cortex_target_assessments
114
- SET dossier = $1::jsonb,
115
- status = 'completed',
116
- progress = 'Completed',
117
- error = NULL,
118
- completed_at = NOW(),
119
- updated_at = NOW()
120
- WHERE id = $2 AND status != 'deleted'`,
121
- values: [JSON.stringify(dossier), id],
122
- });
123
- });
124
- }
125
- export function markFailed(pool, id, error) {
126
- return tryMutation("targetAssessments.markFailed", async () => {
127
- await pool.query({
128
- text: `UPDATE cortex_target_assessments
129
- SET status = 'failed',
130
- error = $1::jsonb,
131
- completed_at = NOW(),
132
- updated_at = NOW()
133
- WHERE id = $2 AND status != 'deleted'`,
134
- values: [JSON.stringify(error), id],
135
- });
136
- });
137
- }
138
- /**
139
- * Flip the row to the new `suspended_insufficient_funds` status used when
140
- * an LLM step self-cancels the workflow on a 402 (budget exhausted).
141
- * Idempotent on DBOS replay — a second call from the terminal handler on
142
- * the recovery path is a no-op against an already-suspended row. Soft-
143
- * deleted rows are NOT resurrected.
144
- */
145
- export function markAssessmentSuspended(pool, id) {
146
- return tryMutation("targetAssessments.markAssessmentSuspended", async () => {
147
- await pool.query({
148
- text: `UPDATE cortex_target_assessments
149
- SET status = 'suspended_insufficient_funds',
150
- progress = 'Suspended — insufficient funds',
151
- updated_at = NOW()
152
- WHERE id = $1 AND status != 'deleted'`,
153
- values: [id],
154
- });
155
- });
156
- }
157
- /**
158
- * Flip a suspended row back to `running` on `DBOS.resumeWorkflow`. Called
159
- * from the resume entry point AFTER the resume call returns success.
160
- * No-op on a soft-deleted row.
161
- */
162
- export function markAssessmentRunning(pool, id) {
163
- return tryMutation("targetAssessments.markAssessmentRunning", async () => {
164
- await pool.query({
165
- text: `UPDATE cortex_target_assessments
166
- SET status = 'running',
167
- progress = 'Resuming',
168
- error = NULL,
169
- updated_at = NOW()
170
- WHERE id = $1 AND status != 'deleted'`,
171
- values: [id],
172
- });
173
- });
174
- }
175
- export function getAssessment(pool, id, organizationId) {
176
- // The raw read is wrapped; `mapRow`/`mapListRow` runs via `.map()` outside
177
- // the catch so its corrupt-row throw (missing created_at) propagates as a
178
- // control-flow throw rather than being captured as a `DbError`.
179
- return tryQuery("targetAssessments.getAssessment", async () => {
180
- const result = await pool.query({
181
- text: `SELECT ${SELECT_COLUMNS}
182
- FROM cortex_target_assessments
183
- WHERE id = $1 AND organization_id = $2`,
184
- values: [id, organizationId],
185
- });
186
- return result.rows[0] ?? null;
187
- }).map((row) => (row ? mapRow(row) : null));
188
- }
189
- export function listAssessmentsByOrg(pool, organizationId, options = {}) {
190
- const limit = options.limit ?? 50;
191
- const offset = options.offset ?? 0;
192
- const includeDeleted = options.includeDeleted ?? false;
193
- const filterDeleted = includeDeleted ? "" : `AND status != 'deleted'`;
194
- // `mapListRow` runs via `.map()` outside the catch so its corrupt-row throw
195
- // propagates as a control-flow throw, not a captured `DbError`.
196
- return tryQuery("targetAssessments.listAssessmentsByOrg", async () => {
197
- const result = await pool.query({
198
- text: `SELECT ${SELECT_LIST_COLUMNS}
199
- FROM cortex_target_assessments
200
- WHERE organization_id = $1 ${filterDeleted}
201
- ORDER BY created_at DESC
202
- LIMIT $2 OFFSET $3`,
203
- values: [organizationId, limit, offset],
204
- });
205
- return result.rows;
206
- }).map((rows) => rows.map(mapListRow));
207
- }
208
- /**
209
- * Soft-delete a row. A missing/already-deleted row returns `ok(0)` — absence
210
- * rides the ok channel; `rowCount === 0` is not a failure.
211
- */
212
- export function softDeleteAssessment(pool, id, organizationId) {
213
- return tryMutation("targetAssessments.softDeleteAssessment", async () => {
214
- const result = await pool.query({
215
- text: `UPDATE cortex_target_assessments
216
- SET status = 'deleted', updated_at = NOW()
217
- WHERE id = $1 AND organization_id = $2`,
218
- values: [id, organizationId],
219
- });
220
- return result.rowCount ?? 0;
221
- });
222
- }