@trazum/cli 1.30.0 → 1.32.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.
package/src/markdown.ts CHANGED
@@ -723,6 +723,16 @@ export interface ProfileMarkdownInput {
723
723
  levers: BillLevers;
724
724
  cache: CacheEconomics;
725
725
  t: CliMessages;
726
+ /**
727
+ * The gate verdicts, when any gate was armed.
728
+ *
729
+ * They reached the terminal on stderr and stopped there, so a CI run
730
+ * summary carried the whole report and not the one sentence explaining why
731
+ * the build was red — the reader had to open the raw log to find it. These
732
+ * arrive already rendered by the caller, which owns the thresholds and the
733
+ * copy; this is a rendering and must not decide anything a gate decides.
734
+ */
735
+ gates?: { failed: boolean; lines: string[] };
726
736
  /**
727
737
  * The `--since`/`--until` values as the user typed them, when a window was
728
738
  * applied. Passed through rather than re-derived from `timeWindow`'s epoch
@@ -783,7 +793,7 @@ export interface ProfileMarkdownInput {
783
793
  * reading CI instead of machines.
784
794
  */
785
795
  export function renderProfileMarkdown(input: ProfileMarkdownInput): string {
786
- const { report, levers, cache, t, window, stalePricing, against, whatIf, pressure = [] } = input;
796
+ const { report, levers, cache, t, window, stalePricing, against, whatIf, pressure = [], gates } = input;
787
797
  const n = (value: number): string => value.toLocaleString(t.numberLocale);
788
798
  const pct = (share: number): string => `${(share * 100).toFixed(1)}%`;
789
799
  const shares = sharesOf(report.total);
@@ -793,6 +803,24 @@ export function renderProfileMarkdown(input: ProfileMarkdownInput): string {
793
803
  const lines: string[] = [];
794
804
  lines.push(`### ${t.profile.heading()}`);
795
805
  lines.push('');
806
+ /**
807
+ * The verdict first, when a gate was armed.
808
+ *
809
+ * A run summary that carried the whole report and not the sentence
810
+ * explaining why the build is red made the reader open the raw log to find
811
+ * it — which is the same failure the explanation itself exists to fix, one
812
+ * surface further out. A failure is quoted so it survives being skimmed; a
813
+ * pass is stated plainly and does not shout.
814
+ */
815
+ if (gates !== undefined && gates.lines.length > 0) {
816
+ // One mark, on the verdict. The lines under it explain that verdict and
817
+ // are not themselves failures — marking each would turn one red build
818
+ // into a wall of crosses and make the actual verdict harder to find.
819
+ const [verdict, ...rest] = gates.lines;
820
+ lines.push(gates.failed ? `> ❌ **${mdText(verdict!)}**` : `_${mdText(verdict!)}_`);
821
+ for (const line of rest) lines.push(gates.failed ? `> ${mdText(line)}` : `_${mdText(line)}_`);
822
+ lines.push('');
823
+ }
796
824
  lines.push(`**${t.profile.spent(t.profile.calls(report.total.calls), formatUsd(report.total.totalUsd))}**`);
797
825
  lines.push('');
798
826
  // The span, under the same rule as the terminal: stated, never extrapolated,
@@ -1029,8 +1057,19 @@ export function renderProfileMarkdown(input: ProfileMarkdownInput): string {
1029
1057
  `**${mdText(t.profile.whatIfTotal(formatUsd(whatIf.currentUsd), formatUsd(whatIf.targetUsd), formatUsd(Math.abs(whatIf.deltaUsd))))}**`,
1030
1058
  );
1031
1059
  lines.push('');
1060
+ // The decision's other half, on the target's rates, never summed.
1061
+ if (whatIf.batchOnTarget !== null) {
1062
+ lines.push(`_${mdText(t.profile.whatIfBatchOnTarget(formatUsd(whatIf.batchOnTarget.targetUsd), formatUsd(whatIf.targetUsd)))}_`);
1063
+ lines.push('');
1064
+ }
1032
1065
  for (const slice of whatIf.slices.slice(0, 5)) {
1033
1066
  lines.push(`- ${mdText(t.profile.whatIfSlice(showLabel(slice.label), slice.model, formatUsd(slice.currentUsd), formatUsd(slice.targetUsd)))}`);
1067
+ // The cache-minimum caveat, in place — the row above flatters the move.
1068
+ if (slice.cacheBeyondTarget !== null) {
1069
+ lines.push(
1070
+ ` - ⚠️ ${mdText(t.profile.whatIfCacheBeyond(n(slice.maxCallInputTokens), n(slice.cacheBeyondTarget.minTokens), formatUsd(slice.cacheBeyondTarget.noCacheUsd)))}`,
1071
+ );
1072
+ }
1034
1073
  }
1035
1074
  lines.push('');
1036
1075
  }