@allurereport/plugin-agent 3.13.2 → 3.14.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.
@@ -8,10 +8,10 @@ export declare const ENRICHMENT_ACTIONS_BY_CHECK_NAME: Record<string, Enrichment
8
8
  export declare const AGENT_WORKFLOWS_MARKDOWN = "Use the smallest workflow that matches the task. Each workflow has the same shape: when to use it, which agent-mode commands help, and what must be true before you call the task done.\n\n### Validate A Change\n\nUse when code or tests changed and you need a user-facing safety conclusion. For small mechanical changes, use this same workflow with narrower expectations rather than a separate shortcut.\n\nCommands:\n\n- `allure agent --goal <text> --expect-* -- <command>`\n\nDone when:\n\n- the expected scope ran and no forbidden scope appeared\n- `index.md`, `manifest/run.json`, `manifest/tests.jsonl`, and `manifest/findings.jsonl` were reviewed\n- the `index.md` path was reported\n- the changed package build and required static checks passed when this repository guide requires them\n\n### Add Or Update Tests\n\nUse when creating or changing tests for a feature, fix, or behavior gap.\n\nCommands:\n\n- `allure agent --goal <text> --expect-tests <count> --expect-test \"<fullName>\" --expect-label name=value --expect-step-containing <text> -- <command>`\n\nDone when:\n\n- the tests prove the intended behavior rather than only touching the code path\n- scope expectations match the intended feature, issue, or package slice\n- each expected test has enough steps or attachments for a reviewer to understand what happened\n- weak evidence, scope drift, and unexpected-test findings are fixed or explicitly accepted as out of scope\n\n### Review Existing Coverage\n\nUse when auditing a package, command matrix, feature area, or business behavior without necessarily changing tests first.\n\nCommands:\n\n- one scoped `allure agent --goal <text> --expect-* -- <command>` run per review group\n- `allure agent inspect --goal <text> --expect-* <allure-results-dir-or-glob>` or `--dump <archive-or-glob>` when the evidence already exists as local results or CI dump artifacts\n\nDone when:\n\n- the audit is split into reviewable groups, or it is explicitly documented as a broad package-health run\n- each group has expectations that describe the intended scope\n- runtime artifacts are reviewed before source-only coverage conclusions\n- uncovered behavior is recorded as follow-up test work instead of being hidden in a broad pass/fail summary\n\n### Review Existing Evidence\n\nUse when CI has already produced dump archives or local Allure results already exist and you need agent-readable review artifacts without rerunning tests locally.\n\nCommands:\n\n- `allure agent inspect <allure-results-dir-or-glob>`\n- `allure agent inspect --dump <archive-or-glob>`\n- `allure agent inspect --dump <linux.zip> --dump <macos.zip>`\n- `allure agent inspect --goal <text> --expect-* --dump <archive-or-glob>`\n\nDone when:\n\n- all intended result directories or dump artifacts were downloaded or present and matched by the command\n- `index.md`, `manifest/run.json`, `manifest/tests.jsonl`, and `manifest/findings.jsonl` were reviewed\n- the review calls out that inspect-derived output cannot add missing live process logs or rerun-time evidence unless those artifacts were captured in the results or dumps\n- any environment-specific gaps between CI jobs are explicit\n\n### Triage Failures\n\nUse when tests failed, broke, or runner output does not match agent artifacts.\n\nCommands:\n\n- `allure agent latest`\n- `allure agent --rerun-latest --rerun-preset failed -- <command>`\n- `allure agent --rerun-from <output-dir> --rerun-preset failed -- <command>`\n\nDone when:\n\n- failing, broken, or unmodeled runner-visible failures are represented in agent artifacts, or partial modeling is called out explicitly\n- `artifacts/global/stderr.txt` and global errors were checked when failures are missing from `manifest/tests.jsonl`\n- reruns use prior agent output instead of hand-built runner test names whenever the runner can consume the generated test plan\n\n### Rerun A Prior Scope\n\nUse when prior agent output already identifies failed, unsuccessful, or review-targeted tests and the next run should stay focused.\n\nCommands:\n\n- `allure agent select --latest [--preset review|failed|unsuccessful|all]`\n- `allure agent select --from <output-dir> [--environment <id>] [--label name=value]`\n- `allure agent --rerun-latest -- <command>`\n- `allure agent --rerun-from <output-dir> -- <command>`\n\nDone when:\n\n- the rerun scope comes from Allure testplan support\n- `--rerun-preset`, `--rerun-environment`, or `--rerun-label` filters explain any narrowed selection\n- manual test names are used only as a fallback when testplan support is unavailable\n- the rerun output is reviewed before making a new conclusion\n\n### Improve Evidence Quality\n\nUse when tests pass or fail but the runtime story is too weak to review.\n\nCommands:\n\n- `allure agent --expect-step-containing <text> --expect-steps <count> --expect-attachments <count> -- <command>`\n- `allure agent --expect-attachment <name|name=value|content-type=value> -- <command>`\n\nDone when:\n\n- steps describe real setup, actions, state transitions, or assertions\n- attachments contain runtime evidence such as payloads, responses, screenshots, DOM snapshots, diffs, logs, or traces\n- placeholder steps, generic `\"passed\"` attachments, and other dummy evidence are removed\n- the same intended scope was rerun and no high-confidence evidence findings remain\n\n### Recover Or Diagnose Agent Mode\n\nUse when agent output is missing, the latest run cannot be found, local CLI support is unclear, or state behaves differently in CI or a sandbox.\n\nCommands:\n\n- `allure --version`\n- `allure agent capabilities --json`\n- `allure agent --help`\n- `allure agent latest`\n- `allure agent state-dir`\n- `ALLURE_AGENT_STATE_DIR=<dir>`\n\nDone when:\n\n- supported local commands and flags are known from capabilities or help output\n- the output directory, `index.md` path, or state directory is identified, or the reason it is unavailable is documented\n- console-only conclusions stay provisional until agent-mode artifacts are available";
9
9
  export declare const AGENT_COMMAND_TASK_MAP: readonly ["`allure --version`, `allure agent capabilities --json`, and `allure agent --help`: setup and capability-detection loop. Use when the local CLI surface is unknown, generated guidance may be stale, or you need to choose supported flags without guessing.", "`allure agent --goal ... -- <command>`: test review, feature delivery, smoke-check, and coverage loops. Use when a test command needs runtime evidence, scope expectations, and user-facing conclusions based on agent artifacts rather than console output alone. The default `--report auto` may also write a human-readable `awesome/index.html` for small runs.", "`allure agent inspect <allure-results-dir-or-glob>` / `allure agent inspect --dump <archive-or-glob>`: existing evidence review loop. Use after downloading one or more dump archives or when Allure results already exist and you need agent-readable markdown, manifests, and optional human report output without rerunning tests locally. Repeat `--dump` to merge multiple environments or jobs.", "`allure agent latest`: output recovery loop. Use when a previous run omitted `--output`, you need the newest output directory and `index.md` path, a user asks for the human-readable report from the last run, or a follow-up task needs prior output before selecting or rerunning tests.", "`allure agent state-dir`: tooling diagnosis loop. Use when `latest` cannot find a run, CI or sandbox state looks wrong, or you need to explain where per-project run registries are stored.", "`allure agent query --latest summary|tests|findings|test` / `allure agent query --from <output-dir> ...`: output inspection loop. Use when you need a focused JSON summary, human-report status, filtered tests, filtered findings, or one test from prior agent output without manually loading raw manifests first.", "`allure agent select --latest` / `allure agent select --from <output-dir>`: rerun-planning loop. Use when you need to inspect, filter, or write the Allure test plan from prior results before executing another run. Without `--output`, stdout is raw testplan JSON; with `--output`, stdout summarizes the file path, source output, preset, and selected count.", "`allure agent --rerun-latest` / `allure agent --rerun-from <output-dir>`: focused retry loop. Use when prior output already identifies failed, unsuccessful, or review-targeted tests and you should rerun that slice through Allure testplan support instead of rebuilding runner-specific test names.", "`ALLURE_AGENT_STATE_DIR=<dir>`: CI and sandbox state-control loop. Use when multiple jobs need a deterministic state location, the default temp state is not shared, or the default state directory is not writable."];
10
10
  export declare const AGENT_VERIFICATION_RULES: readonly ["If a command executes tests and its result will be used for smoke checking, reasoning, review, coverage analysis, debugging, or any user-facing conclusion, run it through `allure agent`. It preserves the original console logs and adds agent-mode artifacts without inheriting the normal report or export plugins from the project config.", "Use `allure agent` for smoke checks too, even when the change is small or mechanical.", "Only skip agent mode when it is impossible or when you are debugging agent mode itself.", "After changing a package in this repository, run that package build command before finalizing (for example, `yarn workspace <package-name> build`).", "After each agent-mode test run, print the `index.md` path from that run's output directory so users can open the run overview quickly."];
11
- export declare const AGENT_TEST_ENRICHMENT_BEST_PRACTICES: readonly ["Steps must wrap real actions, state transitions, or assertions. Prefer a small setup/action/assertion narrative over event-by-event step spam.", "Attachments must capture real runtime evidence from that execution: payloads, responses, screenshots, DOM snapshots, diffs, logs, or traces.", "Add metadata only when it improves scope review, debugging, or downstream policy. Keep labels and parameters intentionally minimal.", "If multiple call sites need the same evidence, instrument the helper once. Example: teach `runCommand` to emit a step instead of wrapping every `runCommand(...)` call site with identical step blocks."];
11
+ export declare const AGENT_TEST_ENRICHMENT_BEST_PRACTICES: readonly ["Steps should wrap real actions, state transitions, or assertions; let the test's nature decide how granular they are.", "Attachments must capture real runtime evidence from that execution: payloads, responses, screenshots, DOM snapshots, diffs, logs, or traces.", "Add metadata only when it improves scope review, debugging, or downstream policy. Keep labels and parameters intentionally minimal.", "If multiple call sites need the same evidence, instrument the helper once. Example: teach `runCommand` to emit a step instead of wrapping every `runCommand(...)` call site with identical step blocks."];
12
12
  export declare const AGENT_ANTI_DUMMY_POLICY: readonly ["Do not add empty wrapper steps, placeholder attachments, or generic strings such as `passed`, `success`, or static boilerplate.", "Do not attach evidence that is not grounded in the current test execution. Synthetic summaries are fine only when they are derived from real runtime artifacts.", "Do not inflate metadata with labels, stories, or severities that no expectation, review step, or policy actually uses."];
13
- export declare const AGENT_ACCEPTANCE_CHECKLIST: readonly ["The rerun matches the intended scope and does not trigger forbidden or unexpected-test findings.", "Each touched test shows enough evidence to explain what happened and what was verified.", "Retries include per-attempt diagnostics when the same test reruns.", "No high-confidence anti-dummy findings remain, especially `noop-dominated-steps` or low-signal `step-spam` traces."];
13
+ export declare const AGENT_ACCEPTANCE_CHECKLIST: readonly ["The rerun matches the intended scope and does not trigger forbidden or unexpected-test findings.", "Each touched test shows enough evidence to explain what happened and what was verified.", "Retries include per-attempt diagnostics when the same test reruns."];
14
14
  export declare const AGENT_REVIEW_COMPLETENESS_CHECKLIST: readonly ["The relevant scope was run with agent mode unless that was impossible.", "Scoped reviews created expectations before the final rerun, unless the task was an intentionally broad package-health audit.", "The final conclusion was based on `manifest/run.json`, `manifest/test-events.jsonl`, `index.md`, `manifest/tests.jsonl`, and `manifest/findings.jsonl` rather than console output alone.", "Any missing or partial runtime modeling was called out explicitly before the review was considered complete."];
15
15
  export declare const AGENT_PARTIAL_RUNTIME_REVIEW_RULES: readonly ["Suite-load, import, or setup failures may appear only in `artifacts/global/stderr.txt` or global errors instead of `manifest/tests.jsonl`.", "If runner-visible failures are not represented as logical tests, inspect global stderr and treat the run as a partial runtime review, not a clean result set.", "Console-only conclusions stay provisional when agent output is missing, incomplete, or partially modeled."];
16
- export declare const AGENT_INSTRUCTIONS_TEMPLATE = "## Allure Agent Mode Instructions\n\n- If a command executes tests and its result will be used for smoke checking, reasoning, review, coverage analysis, debugging, or any user-facing conclusion, run it through `allure agent`. It preserves the original console logs and adds agent-mode artifacts without inheriting the normal report or export plugins from the project config.\n- Use `allure agent` for smoke checks too, even when the change is small or mechanical.\n- Only skip agent mode when it is impossible or when you are debugging agent mode itself.\n- After each agent-mode test run, print the `index.md` path from that run's output directory so users can open the run overview quickly.\n- Use `allure agent latest` to print the newest output directory and `index.md` path when `--output` was omitted.\n- When a user asks for the human-readable report from the last run, use `allure agent latest` first if the output directory is unknown, then check `manifest/human-report.json`; when its status is `generated`, use the path recorded there, usually `awesome/index.html`.\n- Use `allure agent capabilities --json` when you need structured supported-command, expectation, output, rerun, and unsupported-feature data without scraping help text.\n- Use `allure agent state-dir` to inspect the shared state directory that stores per-project run registries.\n- Use `allure agent latest`, `state-dir`, `query`, `select`, and `--rerun-*` according to their loop/task/problem mapping instead of treating them as interchangeable helper commands.\n- Use `allure agent inspect <allure-results-dir-or-glob>` or `allure agent inspect --dump <archive-or-glob>` when you need agent-readable markdown and manifests from existing Allure results without rerunning tests locally; repeat `--dump` for multiple CI jobs or environments.\n- Use `--report auto|off|awesome|config` to control human report output. The default `auto` mode writes `awesome/index.html` for 1000 or fewer stored visible logical results and records generated, skipped, disabled, or failed status in `manifest/human-report.json`.\n- Use `allure agent query --latest summary|tests|findings|test` or `allure agent query --from <output-dir> ...` to inspect prior output as focused JSON before manually opening raw manifests.\n- Use `allure agent select --latest` or `allure agent select --from <output-dir>` to inspect the review-targeted test plan before rerunning; add `--output <file>` when you want the CLI to write the plan and print a short selection summary.\n- Use `allure agent --rerun-latest -- <command>` or `allure agent --rerun-from <output-dir> -- <command>` to rerun only the selected tests.\n- When rerunning previous failures, use `allure agent --rerun-latest --rerun-preset failed -- <command>` or `allure agent --rerun-from <output-dir> --rerun-preset failed -- <command>` instead of manually rebuilding runner-specific test names.\n- Use `--rerun-preset review|failed|unsuccessful|all`, repeated `--rerun-environment <id>`, and repeated `--rerun-label name=value` when you need a narrower rerun selection from the previous output.\n- Use `ALLURE_AGENT_STATE_DIR` when you need to override the shared agent state directory for `latest`, `state-dir`, or `--rerun-latest`.\n- Prefer inline `allure agent` expectation flags such as `--goal`, `--expect-tests`, `--expect-test`, `--expect-label`, and `--expect-step-containing`; use `--expectations <file>` only when flags become awkward.\n- Run tests with `allure agent` and review `manifest/run.json`, `manifest/test-events.jsonl`, `index.md`, `manifest/tests.jsonl`, and `manifest/findings.jsonl`.\n- Enrich only the intended tests. Add real steps for real setup, actions, and assertions.\n- Attach only real runtime evidence such as payloads, responses, screenshots, DOM snapshots, diffs, logs, or traces.\n- Keep metadata minimal. Add labels or severity only when scope review, debugging, or quality policy uses them.\n- Instrument stable helpers when several call sites need the same evidence. For example, teach `runCommand` to emit a step instead of wrapping every caller.\n- Reject the rerun if scope drifts, evidence stays weak, or high-confidence noop-style findings remain.";
16
+ export declare const AGENT_INSTRUCTIONS_TEMPLATE = "## Allure Agent Mode Instructions\n\n- If a command executes tests and its result will be used for smoke checking, reasoning, review, coverage analysis, debugging, or any user-facing conclusion, run it through `allure agent`. It preserves the original console logs and adds agent-mode artifacts without inheriting the normal report or export plugins from the project config.\n- Use `allure agent` for smoke checks too, even when the change is small or mechanical.\n- Only skip agent mode when it is impossible or when you are debugging agent mode itself.\n- After each agent-mode test run, print the `index.md` path from that run's output directory so users can open the run overview quickly.\n- Use `allure agent latest` to print the newest output directory and `index.md` path when `--output` was omitted.\n- When a user asks for the human-readable report from the last run, use `allure agent latest` first if the output directory is unknown, then check `manifest/human-report.json`; when its status is `generated`, use the path recorded there, usually `awesome/index.html`.\n- Use `allure agent capabilities --json` when you need structured supported-command, expectation, output, rerun, and unsupported-feature data without scraping help text.\n- Use `allure agent state-dir` to inspect the shared state directory that stores per-project run registries.\n- Use `allure agent latest`, `state-dir`, `query`, `select`, and `--rerun-*` according to their loop/task/problem mapping instead of treating them as interchangeable helper commands.\n- Use `allure agent inspect <allure-results-dir-or-glob>` or `allure agent inspect --dump <archive-or-glob>` when you need agent-readable markdown and manifests from existing Allure results without rerunning tests locally; repeat `--dump` for multiple CI jobs or environments.\n- Use `--report auto|off|awesome|config` to control human report output. The default `auto` mode writes `awesome/index.html` for 1000 or fewer stored visible logical results and records generated, skipped, disabled, or failed status in `manifest/human-report.json`.\n- Use `allure agent query --latest summary|tests|findings|test` or `allure agent query --from <output-dir> ...` to inspect prior output as focused JSON before manually opening raw manifests.\n- Use `allure agent select --latest` or `allure agent select --from <output-dir>` to inspect the review-targeted test plan before rerunning; add `--output <file>` when you want the CLI to write the plan and print a short selection summary.\n- Use `allure agent --rerun-latest -- <command>` or `allure agent --rerun-from <output-dir> -- <command>` to rerun only the selected tests.\n- When rerunning previous failures, use `allure agent --rerun-latest --rerun-preset failed -- <command>` or `allure agent --rerun-from <output-dir> --rerun-preset failed -- <command>` instead of manually rebuilding runner-specific test names.\n- Use `--rerun-preset review|failed|unsuccessful|all`, repeated `--rerun-environment <id>`, and repeated `--rerun-label name=value` when you need a narrower rerun selection from the previous output.\n- Use `ALLURE_AGENT_STATE_DIR` when you need to override the shared agent state directory for `latest`, `state-dir`, or `--rerun-latest`.\n- Prefer inline `allure agent` expectation flags such as `--goal`, `--expect-tests`, `--expect-test`, `--expect-label`, and `--expect-step-containing`; use `--expectations <file>` only when flags become awkward.\n- Run tests with `allure agent` and review `manifest/run.json`, `manifest/test-events.jsonl`, `index.md`, `manifest/tests.jsonl`, and `manifest/findings.jsonl`.\n- Enrich only the intended tests. Add real steps for real setup, actions, and assertions.\n- Attach only real runtime evidence such as payloads, responses, screenshots, DOM snapshots, diffs, logs, or traces.\n- Keep metadata minimal. Add labels or severity only when scope review, debugging, or quality policy uses them.\n- Instrument stable helpers when several call sites need the same evidence. For example, teach `runCommand` to emit a step instead of wrapping every caller.\n- Reject the rerun if scope drifts or evidence stays weak.";
17
17
  export declare const renderAgentsGuide: () => string;
package/dist/guidance.js CHANGED
@@ -94,11 +94,6 @@ export const ENRICHMENT_ACTIONS_BY_CHECK_NAME = {
94
94
  title: "Repair logical test identity",
95
95
  guidance: "Use stable, unique history IDs so distinct logical tests do not collapse into one file.",
96
96
  },
97
- "failed-without-useful-steps": {
98
- category: "add-meaningful-steps",
99
- title: "Add meaningful setup, action, and assertion steps",
100
- guidance: "Wrap only real actions, state transitions, and checks in Allure steps before rerunning.",
101
- },
102
97
  "expected-step-containing-missing": {
103
98
  category: "add-meaningful-steps",
104
99
  title: "Add or correct the expected step text",
@@ -119,41 +114,6 @@ export const ENRICHMENT_ACTIONS_BY_CHECK_NAME = {
119
114
  title: "Add the required attachment",
120
115
  guidance: "Attach the requested runtime artifact near the relevant action or assertion.",
121
116
  },
122
- "failed-without-attachments": {
123
- category: "add-test-attachments",
124
- title: "Attach focused runtime evidence near the failure",
125
- guidance: "Add real payloads, responses, screenshots, DOM snapshots, diffs, or logs near the failing point.",
126
- },
127
- "nontrivial-run-with-empty-trace": {
128
- category: "add-meaningful-steps",
129
- title: "Make the execution path observable",
130
- guidance: "Expose the real setup, action, and verification path with steps or attachments on the next run.",
131
- },
132
- "retries-without-new-evidence": {
133
- category: "add-retry-diagnostics",
134
- title: "Capture what changes between retries",
135
- guidance: "Add per-attempt diagnostics so retries show new evidence instead of repeating the same trace.",
136
- },
137
- "noop-dominated-steps": {
138
- category: "collapse-low-signal-trace",
139
- title: "Replace noop-style steps with real evidence",
140
- guidance: "Keep only steps tied to real actions or checks, and replace bulk event spam with a compact artifact when needed.",
141
- },
142
- "step-spam": {
143
- category: "collapse-low-signal-trace",
144
- title: "Reduce low-signal step spam",
145
- guidance: "Prefer a smaller set of meaningful steps plus one compact text attachment when the trace is mostly event logs.",
146
- },
147
- "global-only-artifacts": {
148
- category: "add-test-attachments",
149
- title: "Move evidence closer to the failing test",
150
- guidance: "Use step-scoped or test-scoped attachments near the relevant failing action instead of relying only on global logs.",
151
- },
152
- "passed-without-observable-evidence": {
153
- category: "add-meaningful-steps",
154
- title: "Make the success path reviewable",
155
- guidance: "Add a few real verification steps or attachments so the passing test shows what it proved.",
156
- },
157
117
  };
158
118
  export const AGENT_WORKFLOWS_MARKDOWN = `Use the smallest workflow that matches the task. Each workflow has the same shape: when to use it, which agent-mode commands help, and what must be true before you call the task done.
159
119
 
@@ -308,7 +268,7 @@ export const AGENT_VERIFICATION_RULES = [
308
268
  "After each agent-mode test run, print the `index.md` path from that run's output directory so users can open the run overview quickly.",
309
269
  ];
310
270
  export const AGENT_TEST_ENRICHMENT_BEST_PRACTICES = [
311
- "Steps must wrap real actions, state transitions, or assertions. Prefer a small setup/action/assertion narrative over event-by-event step spam.",
271
+ "Steps should wrap real actions, state transitions, or assertions; let the test's nature decide how granular they are.",
312
272
  "Attachments must capture real runtime evidence from that execution: payloads, responses, screenshots, DOM snapshots, diffs, logs, or traces.",
313
273
  "Add metadata only when it improves scope review, debugging, or downstream policy. Keep labels and parameters intentionally minimal.",
314
274
  "If multiple call sites need the same evidence, instrument the helper once. Example: teach `runCommand` to emit a step instead of wrapping every `runCommand(...)` call site with identical step blocks.",
@@ -322,7 +282,6 @@ export const AGENT_ACCEPTANCE_CHECKLIST = [
322
282
  "The rerun matches the intended scope and does not trigger forbidden or unexpected-test findings.",
323
283
  "Each touched test shows enough evidence to explain what happened and what was verified.",
324
284
  "Retries include per-attempt diagnostics when the same test reruns.",
325
- "No high-confidence anti-dummy findings remain, especially `noop-dominated-steps` or low-signal `step-spam` traces.",
326
285
  ];
327
286
  export const AGENT_REVIEW_COMPLETENESS_CHECKLIST = [
328
287
  "The relevant scope was run with agent mode unless that was impossible.",
@@ -360,7 +319,7 @@ export const AGENT_INSTRUCTIONS_TEMPLATE = `## Allure Agent Mode Instructions
360
319
  - Attach only real runtime evidence such as payloads, responses, screenshots, DOM snapshots, diffs, logs, or traces.
361
320
  - Keep metadata minimal. Add labels or severity only when scope review, debugging, or quality policy uses them.
362
321
  - Instrument stable helpers when several call sites need the same evidence. For example, teach \`runCommand\` to emit a step instead of wrapping every caller.
363
- - Reject the rerun if scope drifts, evidence stays weak, or high-confidence noop-style findings remain.`;
322
+ - Reject the rerun if scope drifts or evidence stays weak.`;
364
323
  const renderBullets = (items) => items.map((item) => `- ${item}`).join("\n");
365
324
  const renderRemediationGuide = () => Object.entries(ENRICHMENT_ACTIONS_BY_CHECK_NAME)
366
325
  .map(([checkName, action]) => `- \`${checkName}\`: ${action.title}. ${action.guidance}`)
package/dist/harness.d.ts CHANGED
@@ -323,8 +323,8 @@ export type AgentReviewOptions = {
323
323
  export declare const DEFAULT_ANTI_DUMMY_CONFIDENCE = 0.75;
324
324
  export declare const AGENT_ENRICHMENT_ACTIONS: Record<string, AgentEnrichmentAction>;
325
325
  export declare const SCOPE_REJECTING_CHECKS: readonly ["expected-test-missing", "expected-count-mismatch", "expected-prefix-missing", "expected-label-missing", "expected-environment-missing", "no-tests-observed", "unexpected-environment", "forbidden-label-observed", "unexpected-test"];
326
- export declare const ITERATION_REQUIRED_CHECKS: readonly ["expectations-invalid", "expectations-empty", "expectations-unsupported-control", "runner-failures-outside-logical-results", "metadata-mismatch", "history-id-collision", "expected-step-containing-missing", "insufficient-expected-steps", "insufficient-expected-attachments", "missing-expected-attachment", "failed-without-useful-steps", "failed-without-attachments", "nontrivial-run-with-empty-trace", "retries-without-new-evidence", "passed-without-observable-evidence"];
327
- export declare const ANTI_DUMMY_CHECKS: readonly ["noop-dominated-steps"];
326
+ export declare const ITERATION_REQUIRED_CHECKS: readonly ["expectations-invalid", "expectations-empty", "expectations-unsupported-control", "runner-failures-outside-logical-results", "metadata-mismatch", "history-id-collision", "expected-step-containing-missing", "insufficient-expected-steps", "insufficient-expected-attachments", "missing-expected-attachment"];
327
+ export declare const ANTI_DUMMY_CHECKS: readonly string[];
328
328
  export declare const buildAgentExpectations: (input: AgentHarnessRequest) => AgentExpectations;
329
329
  export declare const mapFindingToEnrichmentAction: (finding: AgentFindingManifestLine | string) => AgentEnrichmentAction;
330
330
  export declare const loadAgentOutput: (outputDir: string) => Promise<AgentOutputBundle>;
package/dist/harness.js CHANGED
@@ -1,6 +1,9 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { join, resolve } from "node:path";
3
+ import { AgentUsageError } from "./errors.js";
3
4
  import { ENRICHMENT_ACTIONS_BY_CHECK_NAME } from "./guidance.js";
5
+ import { isPathInside } from "./paths.js";
6
+ import { isFileNotFoundError } from "./utils.js";
4
7
  export const DEFAULT_ANTI_DUMMY_CONFIDENCE = 0.75;
5
8
  const FALLBACK_ACTION = {
6
9
  checkName: "*",
@@ -31,13 +34,8 @@ export const ITERATION_REQUIRED_CHECKS = [
31
34
  "insufficient-expected-steps",
32
35
  "insufficient-expected-attachments",
33
36
  "missing-expected-attachment",
34
- "failed-without-useful-steps",
35
- "failed-without-attachments",
36
- "nontrivial-run-with-empty-trace",
37
- "retries-without-new-evidence",
38
- "passed-without-observable-evidence",
39
37
  ];
40
- export const ANTI_DUMMY_CHECKS = ["noop-dominated-steps"];
38
+ export const ANTI_DUMMY_CHECKS = [];
41
39
  const SEVERITY_ORDER = {
42
40
  high: 0,
43
41
  warning: 1,
@@ -142,8 +140,7 @@ const impactForFinding = (finding, antiDummyConfidenceThreshold) => {
142
140
  if (SCOPE_REJECTING_CHECKS.includes(checkName)) {
143
141
  return "reject";
144
142
  }
145
- if (ANTI_DUMMY_CHECKS.includes(checkName) &&
146
- (finding.confidence ?? 0) >= antiDummyConfidenceThreshold) {
143
+ if (ANTI_DUMMY_CHECKS.includes(checkName) && (finding.confidence ?? 0) >= antiDummyConfidenceThreshold) {
147
144
  return "reject";
148
145
  }
149
146
  if (ITERATION_REQUIRED_CHECKS.includes(checkName)) {
@@ -171,16 +168,55 @@ export const mapFindingToEnrichmentAction = (finding) => {
171
168
  const mapped = AGENT_ENRICHMENT_ACTIONS[checkName];
172
169
  return mapped ?? { ...FALLBACK_ACTION, checkName };
173
170
  };
171
+ const resolveContainedOutputPath = (outputDir, relativePath) => {
172
+ const resolved = join(outputDir, relativePath);
173
+ return isPathInside(outputDir, resolved) ? resolved : undefined;
174
+ };
175
+ const readAgentRunManifest = async (outputDir) => {
176
+ const runManifestPath = join(outputDir, "manifest", "run.json");
177
+ let raw;
178
+ try {
179
+ raw = await readFile(runManifestPath, "utf-8");
180
+ }
181
+ catch (error) {
182
+ if (isFileNotFoundError(error)) {
183
+ throw new AgentUsageError(`No Allure agent output found at ${JSON.stringify(outputDir)} (missing manifest/run.json). ` +
184
+ "It must be a directory created by a previous `allure agent` run. " +
185
+ "Run `allure agent latest` to find the most recent output, pass a valid --from <agent-output-dir> or --latest, " +
186
+ "or run `allure agent <command>` to create one.");
187
+ }
188
+ throw error;
189
+ }
190
+ try {
191
+ return JSON.parse(raw);
192
+ }
193
+ catch {
194
+ throw new AgentUsageError(`The Allure agent output at ${JSON.stringify(outputDir)} is corrupted: manifest/run.json is not valid JSON. ` +
195
+ "Re-run `allure agent` to regenerate the output.");
196
+ }
197
+ };
174
198
  export const loadAgentOutput = async (outputDir) => {
175
199
  const absoluteOutputDir = resolve(outputDir);
176
- const run = await readJson(join(absoluteOutputDir, "manifest", "run.json"));
177
- const tests = await readJsonl(join(absoluteOutputDir, "manifest", "tests.jsonl"));
178
- const findings = await readJsonl(join(absoluteOutputDir, "manifest", "findings.jsonl"));
179
- const expected = run.paths.expected_manifest && run.expectations_present
180
- ? await readJson(join(absoluteOutputDir, run.paths.expected_manifest))
200
+ const run = await readAgentRunManifest(absoluteOutputDir);
201
+ let tests;
202
+ let findings;
203
+ try {
204
+ tests = await readJsonl(join(absoluteOutputDir, "manifest", "tests.jsonl"));
205
+ findings = await readJsonl(join(absoluteOutputDir, "manifest", "findings.jsonl"));
206
+ }
207
+ catch {
208
+ throw new AgentUsageError(`The Allure agent output at ${JSON.stringify(absoluteOutputDir)} is incomplete or corrupted: ` +
209
+ "its test/finding manifests could not be read. Re-run `allure agent` to regenerate the output.");
210
+ }
211
+ const expectedManifestPath = run.paths.expected_manifest && run.expectations_present
212
+ ? resolveContainedOutputPath(absoluteOutputDir, run.paths.expected_manifest)
181
213
  : undefined;
182
- const humanReport = run.paths.human_report_manifest
183
- ? await readJson(join(absoluteOutputDir, run.paths.human_report_manifest))
214
+ const expected = expectedManifestPath ? await readJson(expectedManifestPath) : undefined;
215
+ const humanReportManifestPath = run.paths.human_report_manifest
216
+ ? resolveContainedOutputPath(absoluteOutputDir, run.paths.human_report_manifest)
217
+ : undefined;
218
+ const humanReport = humanReportManifestPath
219
+ ? await readJson(humanReportManifestPath)
184
220
  : undefined;
185
221
  return {
186
222
  outputDir: absoluteOutputDir,
@@ -223,9 +259,6 @@ export const planAgentEnrichmentReview = (output, options = {}) => {
223
259
  if (!output.run.expectations_present) {
224
260
  notes.push("Declare inline expectations or provide an expectations file before the next enrichment iteration so scope checks are comparable.");
225
261
  }
226
- if (rejecting.some((item) => item.checkName === "noop-dominated-steps")) {
227
- notes.push("Reject noop-dominated enrichment: keep only steps tied to real actions or checks, and use real runtime attachments instead of placeholders.");
228
- }
229
262
  if (rejecting.some((item) => SCOPE_REJECTING_CHECKS.includes(item.checkName))) {
230
263
  notes.push("Reject scope drift: rerun only the intended tests and keep forbidden scope in the expectations file.");
231
264
  }
package/dist/index.d.ts CHANGED
@@ -4,8 +4,10 @@ export * from "./errors.js";
4
4
  export * from "./harness.js";
5
5
  export * from "./inline-expectations.js";
6
6
  export * from "./invalid-output.js";
7
+ export * from "./output-dir.js";
7
8
  export * from "./paths.js";
8
9
  export * from "./query.js";
9
10
  export * from "./selection.js";
10
11
  export * from "./state.js";
12
+ export * from "./summary.js";
11
13
  export { AgentPlugin as default } from "./plugin.js";
package/dist/index.js CHANGED
@@ -4,8 +4,10 @@ export * from "./errors.js";
4
4
  export * from "./harness.js";
5
5
  export * from "./inline-expectations.js";
6
6
  export * from "./invalid-output.js";
7
+ export * from "./output-dir.js";
7
8
  export * from "./paths.js";
8
9
  export * from "./query.js";
9
10
  export * from "./selection.js";
10
11
  export * from "./state.js";
12
+ export * from "./summary.js";
11
13
  export { AgentPlugin as default } from "./plugin.js";
@@ -44,12 +44,12 @@ const readSingleStringOption = (value, optionName) => {
44
44
  return normalized;
45
45
  };
46
46
  const parseNameValue = (value, optionName, example) => {
47
- const parts = value.split("=");
48
- if (parts.length !== 2) {
47
+ const separatorIndex = value.indexOf("=");
48
+ if (separatorIndex === -1) {
49
49
  throw new AgentExpectationUsageError(`Invalid ${optionName} ${JSON.stringify(value)}. Expected ${example}`, optionName);
50
50
  }
51
- const name = parts[0].trim();
52
- const filterValue = parts[1].trim();
51
+ const name = value.slice(0, separatorIndex).trim();
52
+ const filterValue = value.slice(separatorIndex + 1).trim();
53
53
  if (!name || !filterValue) {
54
54
  throw new AgentExpectationUsageError(`Invalid ${optionName} ${JSON.stringify(value)}. Expected ${example}`, optionName);
55
55
  }
@@ -0,0 +1 @@
1
+ export declare const assertExplicitAgentOutputDirIsSafe: (outputDir: string) => Promise<void>;
@@ -0,0 +1,22 @@
1
+ import { readdir, stat } from "node:fs/promises";
2
+ import { AgentUsageError } from "./errors.js";
3
+ import { isFileNotFoundError } from "./utils.js";
4
+ export const assertExplicitAgentOutputDirIsSafe = async (outputDir) => {
5
+ let stats;
6
+ try {
7
+ stats = await stat(outputDir);
8
+ }
9
+ catch (error) {
10
+ if (isFileNotFoundError(error)) {
11
+ return;
12
+ }
13
+ throw error;
14
+ }
15
+ if (!stats.isDirectory()) {
16
+ throw new AgentUsageError(`--output ${JSON.stringify(outputDir)} is not a directory; use a new or empty directory`);
17
+ }
18
+ if ((await readdir(outputDir)).length > 0) {
19
+ throw new AgentUsageError(`refusing to use --output ${JSON.stringify(outputDir)}: the agent recursively deletes its output directory, ` +
20
+ `so it must be a new or empty directory.`);
21
+ }
22
+ };
package/dist/plugin.js CHANGED
@@ -31,8 +31,6 @@ const FINDING_SEVERITY_ORDER = {
31
31
  info: 2,
32
32
  };
33
33
  const NONTRIVIAL_DURATION_MS = 100;
34
- const STEP_SPAM_THRESHOLD = 25;
35
- const NOOP_RATIO_THRESHOLD = 0.8;
36
34
  const ASSERTION_STEP_PATTERN = /\b(assert|expect|check|verify|validate|should)\b/i;
37
35
  const LOW_VALUE_STDERR_WARNING_PATTERNS = [
38
36
  /\bNO_COLOR\b/i,
@@ -98,7 +96,6 @@ const sortByNewestAttempt = (items) => [...items].sort((left, right) => {
98
96
  const formatTimestamp = (value) => (value === undefined ? "unknown" : new Date(value).toISOString());
99
97
  const formatDurationValue = (value) => formatDuration(value);
100
98
  const escapeJsonPointerSegment = (value) => value.replace(/~/g, "~0").replace(/\//g, "~1");
101
- const isFailedLikeStatus = (status) => status === "failed" || status === "broken";
102
99
  const normalizeStringArray = (value) => {
103
100
  if (!Array.isArray(value)) {
104
101
  return [];
@@ -251,10 +248,12 @@ const analyzeStepTree = (steps) => {
251
248
  if (hasNestedSteps) {
252
249
  summary.nestedSteps += 1;
253
250
  }
254
- if (ASSERTION_STEP_PATTERN.test(node.name)) {
251
+ const isAssertionLike = ASSERTION_STEP_PATTERN.test(node.name);
252
+ const isNonTrivialDuration = (node.duration ?? 0) >= NONTRIVIAL_DURATION_MS;
253
+ if (isAssertionLike) {
255
254
  summary.assertionLikeSteps += 1;
256
255
  }
257
- if (hasEvidence) {
256
+ if (hasEvidence || isAssertionLike || isNonTrivialDuration) {
258
257
  summary.meaningfulSteps += 1;
259
258
  }
260
259
  else {
@@ -304,17 +303,6 @@ const testStepContainsText = (entry, expectedText) => {
304
303
  }
305
304
  return collectStepNames(entry.attempts[0].tr.steps).some(({ name }) => normalizeStepText(name).includes(expected));
306
305
  };
307
- const buildAttemptSignature = (attempt) => JSON.stringify({
308
- status: attempt.tr.status,
309
- errorMessage: attempt.tr.error?.message,
310
- errorTrace: attempt.tr.error?.trace,
311
- stepSummary: attempt.stepSummary,
312
- fixtureSummary: attempt.fixtureStepSummary,
313
- artifactNames: attempt.artifacts.map((artifact) => ({
314
- name: artifact.displayName,
315
- missing: artifact.missing,
316
- })),
317
- });
318
306
  const getPackageName = (tr) => tr.labels.find(({ name }) => name === "package")?.value;
319
307
  const toLabelEntries = (labels) => labels.map((label) => ({
320
308
  name: label.name,
@@ -1028,9 +1016,6 @@ const defaultImpactForFinding = (finding) => {
1028
1016
  ].includes(finding.checkName)) {
1029
1017
  return "reject";
1030
1018
  }
1031
- if (finding.checkName === "noop-dominated-steps" && (finding.confidence ?? 0) >= 0.75) {
1032
- return "reject";
1033
- }
1034
1019
  if ([
1035
1020
  "expectations-invalid",
1036
1021
  "expectations-empty",
@@ -1043,11 +1028,6 @@ const defaultImpactForFinding = (finding) => {
1043
1028
  "runner-failures-outside-logical-results",
1044
1029
  "metadata-mismatch",
1045
1030
  "history-id-collision",
1046
- "failed-without-useful-steps",
1047
- "failed-without-attachments",
1048
- "nontrivial-run-with-empty-trace",
1049
- "retries-without-new-evidence",
1050
- "passed-without-observable-evidence",
1051
1031
  ].includes(finding.checkName)) {
1052
1032
  return "iterate";
1053
1033
  }
@@ -1247,7 +1227,7 @@ const renderExpectationResultSection = (params) => {
1247
1227
  ].join("\n");
1248
1228
  };
1249
1229
  const renderRerunGuidance = (findings) => {
1250
- const relevant = findings.filter(({ category }) => category === "evidence" || category === "smells" || category === "metadata");
1230
+ const relevant = findings.filter(({ category }) => category === "evidence" || category === "metadata");
1251
1231
  if (!relevant.length) {
1252
1232
  return undefined;
1253
1233
  }
@@ -1260,9 +1240,6 @@ const renderRerunGuidance = (findings) => {
1260
1240
  if (relevant.some(({ category }) => category === "metadata")) {
1261
1241
  lines.push("- Add or repair the labels and parameters needed to identify the intended scope.");
1262
1242
  }
1263
- if (relevant.some(({ checkName }) => checkName === "noop-dominated-steps")) {
1264
- lines.push("- Replace repetitive event-style steps with a compact text attachment when the signal is mostly logs.");
1265
- }
1266
1243
  lines.push("- Rerun only the relevant tests with the same expectations so the next review is scoped and comparable.");
1267
1244
  return lines.join("\n");
1268
1245
  };
@@ -1832,7 +1809,7 @@ const computeScopeEvaluation = (params) => {
1832
1809
  scopeMatch: "forbidden",
1833
1810
  reasons: forbidden.reasons,
1834
1811
  expectedReferences: forbidden.references,
1835
- metadataMismatches,
1812
+ metadataMismatches: [],
1836
1813
  };
1837
1814
  }
1838
1815
  if (!positivePresent) {
@@ -1864,18 +1841,9 @@ const buildDurationSummary = (entries) => {
1864
1841
  return {
1865
1842
  total,
1866
1843
  average: durations.length ? Math.round(total / durations.length) : 0,
1867
- max: durations.length ? Math.max(...durations) : 0,
1844
+ max: durations.reduce((acc, value) => (value > acc ? value : acc), 0),
1868
1845
  };
1869
1846
  };
1870
- const collectTestEvidencePaths = (entry) => {
1871
- const paths = [entry.relativePath];
1872
- for (const artifact of entry.allArtifacts) {
1873
- if (artifact.relativePath) {
1874
- paths.push(artifact.relativePath);
1875
- }
1876
- }
1877
- return uniqueValues(paths);
1878
- };
1879
1847
  const getExpectationTargetEntries = (entries, expectations) => {
1880
1848
  if (!hasSelector(expectations.expected)) {
1881
1849
  return entries;
@@ -2162,18 +2130,11 @@ const buildRunAndTestFindings = (params) => {
2162
2130
  ? new Set(getExpectationTargetEntries(entries, expectations).map((entry) => entry.key))
2163
2131
  : new Set();
2164
2132
  for (const entry of entries) {
2165
- const currentAttempt = entry.attempts[0];
2166
- const attemptSignatures = uniqueValues(entry.attempts.map(buildAttemptSignature));
2167
- const testEvidencePaths = collectTestEvidencePaths(entry);
2168
- const allStepSummary = mergeStepSummaries(entry.attempts.map((attempt) => mergeStepSummaries([attempt.stepSummary, attempt.fixtureStepSummary])));
2169
2133
  const expectedEvidenceApplies = expectations ? evidenceTargetKeys.has(entry.key) : false;
2170
2134
  const expectedEvidence = expectations?.evidence;
2171
2135
  const currentStepSummary = currentAttemptStepSummary(entry);
2172
2136
  const currentMeaningfulSteps = currentStepSummary.meaningfulSteps;
2173
2137
  const currentAttachments = nonMissingArtifacts(entry);
2174
- const hasUsefulSteps = currentAttempt.stepSummary.meaningfulSteps + currentAttempt.fixtureStepSummary.meaningfulSteps > 0;
2175
- const hasAnyAttachments = entry.allArtifacts.some((artifact) => !artifact.missing);
2176
- const noopRatio = allStepSummary.totalSteps > 0 ? allStepSummary.noopSteps / allStepSummary.totalSteps : 0;
2177
2138
  if (entry.scope.scopeMatch === "forbidden") {
2178
2139
  const forbiddenLabelReference = entry.scope.expectedReferences.find((reference) => reference.startsWith("forbidden.label_values"));
2179
2140
  const checkName = forbiddenLabelReference ? "forbidden-label-observed" : "forbidden-selector-match";
@@ -2364,124 +2325,6 @@ const buildRunAndTestFindings = (params) => {
2364
2325
  confidence: 0.95,
2365
2326
  }));
2366
2327
  });
2367
- if (isFailedLikeStatus(currentAttempt.tr.status) && !hasUsefulSteps) {
2368
- entry.findings.push(createFinding({
2369
- subject: entry.key,
2370
- subjectType: "test",
2371
- severity: "warning",
2372
- category: "evidence",
2373
- checkName: "failed-without-useful-steps",
2374
- message: "A failed or broken test has no useful runtime steps.",
2375
- explanation: "The failure is recorded, but the step tree does not explain the state transitions that led to it.",
2376
- evidencePaths: testEvidencePaths,
2377
- remediationHint: "Add meaningful steps around the important actions and checks before rerunning.",
2378
- confidence: 0.92,
2379
- }));
2380
- }
2381
- if (isFailedLikeStatus(currentAttempt.tr.status) && !hasAnyAttachments) {
2382
- entry.findings.push(createFinding({
2383
- subject: entry.key,
2384
- subjectType: "test",
2385
- severity: "warning",
2386
- category: "evidence",
2387
- checkName: "failed-without-attachments",
2388
- message: "A failed or broken test has no test-scoped attachments.",
2389
- explanation: "Attachments often provide the fastest route to understanding why the test failed.",
2390
- evidencePaths: testEvidencePaths,
2391
- remediationHint: "Attach targeted logs, payloads, screenshots, or DOM snapshots around the failing point.",
2392
- confidence: 0.88,
2393
- }));
2394
- }
2395
- if ((currentAttempt.tr.duration ?? 0) >= NONTRIVIAL_DURATION_MS &&
2396
- currentAttempt.stepSummary.totalSteps === 0 &&
2397
- currentAttempt.fixtureStepSummary.totalSteps === 0) {
2398
- entry.findings.push(createFinding({
2399
- subject: entry.key,
2400
- subjectType: "test",
2401
- severity: "warning",
2402
- category: "evidence",
2403
- checkName: "nontrivial-run-with-empty-trace",
2404
- message: "A nontrivial test run recorded no steps or fixture activity.",
2405
- explanation: "The duration suggests real work happened, but the trace contains no step-level evidence.",
2406
- evidencePaths: testEvidencePaths,
2407
- remediationHint: "Add runtime steps or attachments so the execution path is observable on the next run.",
2408
- confidence: 0.8,
2409
- }));
2410
- }
2411
- if (entry.attempts.length > 1 && attemptSignatures.length === 1) {
2412
- entry.findings.push(createFinding({
2413
- subject: entry.key,
2414
- subjectType: "test",
2415
- severity: "info",
2416
- category: "evidence",
2417
- checkName: "retries-without-new-evidence",
2418
- message: "Retries did not add any new observable evidence.",
2419
- explanation: "The recorded status, error, steps, and attachments stayed effectively unchanged across retries.",
2420
- evidencePaths: testEvidencePaths,
2421
- remediationHint: "Add retry-specific diagnostics or targeted attachments so reruns can show what changed.",
2422
- confidence: 0.7,
2423
- }));
2424
- }
2425
- if (allStepSummary.totalSteps >= 3 && noopRatio >= NOOP_RATIO_THRESHOLD && !hasAnyAttachments) {
2426
- entry.findings.push(createFinding({
2427
- subject: entry.key,
2428
- subjectType: "test",
2429
- severity: "warning",
2430
- category: "smells",
2431
- checkName: "noop-dominated-steps",
2432
- message: "The step tree is dominated by low-signal steps.",
2433
- explanation: "Most recorded steps are leaf steps without parameters, nested actions, attachments, or error context.",
2434
- evidencePaths: testEvidencePaths,
2435
- remediationHint: "Collapse repetitive event-style steps into a smaller set of meaningful steps or attach a text log instead.",
2436
- confidence: 0.75,
2437
- }));
2438
- }
2439
- if (allStepSummary.totalSteps >= STEP_SPAM_THRESHOLD && !hasAnyAttachments) {
2440
- entry.findings.push(createFinding({
2441
- subject: entry.key,
2442
- subjectType: "test",
2443
- severity: "info",
2444
- category: "smells",
2445
- checkName: "step-spam",
2446
- message: "The trace records many steps but no compact artifact.",
2447
- explanation: "A large number of small steps can be harder to review than a focused log attachment.",
2448
- evidencePaths: testEvidencePaths,
2449
- remediationHint: "Consider attaching a structured text log when the trace is mostly event reporting.",
2450
- confidence: 0.7,
2451
- }));
2452
- }
2453
- if (isFailedLikeStatus(currentAttempt.tr.status) && !hasAnyAttachments && globalArtifacts.length > 0) {
2454
- entry.findings.push(createFinding({
2455
- subject: entry.key,
2456
- subjectType: "test",
2457
- severity: "info",
2458
- category: "smells",
2459
- checkName: "global-only-artifacts",
2460
- message: "Only run-level artifacts are available for this failed test.",
2461
- explanation: "The run captured global logs, but the failed test has no test-scoped attachments to pinpoint the failure.",
2462
- evidencePaths: uniqueValues(testEvidencePaths.concat(globalArtifacts.flatMap((artifact) => (artifact.relativePath ? [artifact.relativePath] : [])))),
2463
- remediationHint: "Prefer step-scoped or test-scoped attachments near the failing action when debugging targeted failures.",
2464
- confidence: 0.78,
2465
- }));
2466
- }
2467
- if (currentAttempt.tr.status === "passed" &&
2468
- (currentAttempt.tr.duration ?? 0) >= NONTRIVIAL_DURATION_MS &&
2469
- currentAttempt.stepSummary.totalSteps === 0 &&
2470
- currentAttempt.fixtureStepSummary.totalSteps === 0 &&
2471
- !hasAnyAttachments) {
2472
- entry.findings.push(createFinding({
2473
- subject: entry.key,
2474
- subjectType: "test",
2475
- severity: "info",
2476
- category: "smells",
2477
- checkName: "passed-without-observable-evidence",
2478
- message: "A nontrivial passing test recorded no observable evidence.",
2479
- explanation: "The test passed, but the agentic output contains no steps, fixture activity, or attachments showing what was verified.",
2480
- evidencePaths: testEvidencePaths,
2481
- remediationHint: "Add a few meaningful verification steps or attachments so the success path is reviewable.",
2482
- confidence: 0.65,
2483
- }));
2484
- }
2485
2328
  }
2486
2329
  return {
2487
2330
  runFindings,
package/dist/query.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { AgentUsageError } from "./errors.js";
4
+ import { isPathInside } from "./paths.js";
4
5
  export const AGENT_QUERY_SCHEMA = "allure-agent-query/v1";
5
6
  export const AGENT_QUERY_VIEWS = ["summary", "tests", "findings", "test"];
6
7
  export const AGENT_TEST_STATUSES = ["failed", "broken", "unknown", "skipped", "passed"];
@@ -83,7 +84,13 @@ const filterAgentQueryFindings = (output, filters) => {
83
84
  .filter((finding) => (filters.checks?.length ? filters.checks.includes(agentFindingCheckName(finding)) : true));
84
85
  };
85
86
  const applyAgentQueryLimit = (items, limit) => limit === undefined ? items : items.slice(0, limit);
86
- const resolveAgentOutputPath = (output, relativePath) => relativePath ? join(output.outputDir, relativePath) : null;
87
+ const resolveAgentOutputPath = (output, relativePath) => {
88
+ if (!relativePath) {
89
+ return null;
90
+ }
91
+ const resolved = join(output.outputDir, relativePath);
92
+ return isPathInside(output.outputDir, resolved) ? resolved : null;
93
+ };
87
94
  const buildAgentQuerySummaryPayload = (output) => ({
88
95
  schema: AGENT_QUERY_SCHEMA,
89
96
  view: "summary",
@@ -153,6 +160,16 @@ const buildAgentQueryTestPayload = async (output, filters) => {
153
160
  const test = matched[0];
154
161
  const markdownPath = resolveAgentOutputPath(output, test.markdown_path);
155
162
  const findings = output.findings.filter((finding) => agentFindingSubjectRef(finding) === test.markdown_path);
163
+ let markdown;
164
+ if (filters.includeMarkdown && markdownPath) {
165
+ try {
166
+ markdown = await readFile(markdownPath, "utf-8");
167
+ }
168
+ catch {
169
+ throw new AgentUsageError(`Could not read the per-test markdown for ${JSON.stringify(test.full_name)} at ${JSON.stringify(markdownPath)}; ` +
170
+ "the agent output is incomplete. Re-run `allure agent` to regenerate it, or omit --include-markdown.");
171
+ }
172
+ }
156
173
  return {
157
174
  schema: AGENT_QUERY_SCHEMA,
158
175
  view: "test",
@@ -160,7 +177,7 @@ const buildAgentQueryTestPayload = async (output, filters) => {
160
177
  markdown_path: markdownPath,
161
178
  test,
162
179
  findings,
163
- ...(filters.includeMarkdown && markdownPath ? { markdown: await readFile(markdownPath, "utf-8") } : {}),
180
+ ...(markdown !== undefined ? { markdown } : {}),
164
181
  };
165
182
  };
166
183
  export const buildAgentQueryPayload = async (output, view, filters) => {
package/dist/selection.js CHANGED
@@ -90,7 +90,8 @@ export const resolveAgentSelectionOutputDir = async (params) => {
90
90
  }
91
91
  const latestState = await readLatestAgentState(cwd);
92
92
  if (!latestState) {
93
- throw new AgentUsageError(`No latest agent output found for ${cwd}`);
93
+ throw new AgentUsageError(`No recorded Allure agent output for ${JSON.stringify(cwd)}. ` +
94
+ "Run `allure agent <command>` first to create one, or pass --from <agent-output-dir>.");
94
95
  }
95
96
  return latestState.outputDir;
96
97
  };
package/dist/state.d.ts CHANGED
@@ -47,6 +47,8 @@ export declare const cleanupAgentRunState: (params: {
47
47
  cwd: string;
48
48
  currentRunId?: string;
49
49
  keepManagedRuns?: number;
50
+ managedOutputGraceMs?: number;
51
+ now?: number;
50
52
  }) => Promise<AgentStateCleanupResult>;
51
53
  export declare const cleanupStaleAgentRunStates: (params: {
52
54
  cwd: string;
package/dist/state.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { appendFile, readFile, rm } from "node:fs/promises";
2
2
  import { join, resolve } from "node:path";
3
+ import process from "node:process";
3
4
  import { isFileNotFoundError, listAgentManagedTempOutputDirs, listAgentStatePaths, pathExists, projectStatePath, readPathMtimeMs, tryWithAgentStateLock, withAgentStateLock, writeJsonlAtomic, } from "./utils.js";
4
5
  export { ALLURE_AGENT_STATE_DIR_ENV, resolveAgentStateDir } from "./utils.js";
5
6
  const AGENT_RUN_STATE_SCHEMA = "allure-agent-run/v1";
6
7
  const AGENT_STALE_OUTPUT_TTL_MS = 7 * 24 * 60 * 60 * 1000;
8
+ const AGENT_MANAGED_OUTPUT_GRACE_MS = 60 * 60 * 1000;
7
9
  const isAgentRunState = (value) => {
8
10
  if (typeof value !== "object" || value === null) {
9
11
  return false;
@@ -86,7 +88,17 @@ const foldAgentRunStates = (states) => {
86
88
  .sort((a, b) => a.startedAt - b.startedAt || (a.finishedAt ?? a.startedAt) - (b.finishedAt ?? b.startedAt));
87
89
  };
88
90
  const getAgentRunStateAgeTimestamp = (state) => state.finishedAt ?? state.startedAt;
89
- const isManagedOutputStale = (state, now, staleOutputTtlMs) => state.managedOutput && now - getAgentRunStateAgeTimestamp(state) >= staleOutputTtlMs;
91
+ const isProcessAlive = (pid) => {
92
+ try {
93
+ process.kill(pid, 0);
94
+ return true;
95
+ }
96
+ catch (error) {
97
+ return error.code === "EPERM";
98
+ }
99
+ };
100
+ const isAgentRunActive = (state) => state.status === "running" && state.pid !== undefined && isProcessAlive(state.pid);
101
+ const isManagedOutputStale = (state, now, staleOutputTtlMs) => state.managedOutput && !isAgentRunActive(state) && now - getAgentRunStateAgeTimestamp(state) >= staleOutputTtlMs;
90
102
  const isAgentOutputDirectory = async (outputDir) => (await pathExists(join(outputDir, "manifest", "run.json"))) || (await pathExists(join(outputDir, "index.md")));
91
103
  const cleanupStaleAgentRunState = async (params) => {
92
104
  const states = foldAgentRunStates(await readAgentRunStateLines(params.cwd));
@@ -181,6 +193,8 @@ export const readLatestAgentState = async (cwd) => {
181
193
  };
182
194
  export const cleanupAgentRunState = async (params) => withAgentStateLock(params.cwd, async () => {
183
195
  const keepManagedRuns = Math.max(0, params.keepManagedRuns ?? 1);
196
+ const managedOutputGraceMs = Math.max(0, params.managedOutputGraceMs ?? AGENT_MANAGED_OUTPUT_GRACE_MS);
197
+ const now = params.now ?? Date.now();
184
198
  const states = foldAgentRunStates(await readAgentRunStateLines(params.cwd));
185
199
  const existing = [];
186
200
  for (const state of states) {
@@ -202,6 +216,9 @@ export const cleanupAgentRunState = async (params) => withAgentStateLock(params.
202
216
  if (!state.managedOutput || state.status !== "finished" || retainedManagedRunIds.has(state.runId)) {
203
217
  continue;
204
218
  }
219
+ if (now - (state.finishedAt ?? state.startedAt) <= managedOutputGraceMs) {
220
+ continue;
221
+ }
205
222
  try {
206
223
  await rm(state.outputDir, { recursive: true, force: true });
207
224
  deleted.push(state);
@@ -0,0 +1,9 @@
1
+ import type { AgentRunManifest } from "./harness.js";
2
+ import type { AgentHumanReportStatus } from "./model.js";
3
+ export declare const formatAgentRunSummary: (params: {
4
+ outputDir: string;
5
+ run: AgentRunManifest;
6
+ humanReport?: AgentHumanReportStatus | null;
7
+ durationMs?: number;
8
+ rerunCommand?: string;
9
+ }) => string[];
@@ -0,0 +1,73 @@
1
+ import { join } from "node:path";
2
+ const formatStatusCounts = (stats) => {
3
+ const parts = [
4
+ stats.failed ? `${stats.failed} failed` : undefined,
5
+ stats.broken ? `${stats.broken} broken` : undefined,
6
+ stats.passed ? `${stats.passed} passed` : undefined,
7
+ stats.skipped ? `${stats.skipped} skipped` : undefined,
8
+ stats.unknown ? `${stats.unknown} unknown` : undefined,
9
+ ].filter((part) => part !== undefined);
10
+ const total = stats.total ?? 0;
11
+ return `${parts.length ? parts.join(", ") : "no tests"} (${total} total)`;
12
+ };
13
+ const formatDurationMs = (ms) => {
14
+ if (ms < 1000) {
15
+ return `${Math.round(ms)}ms`;
16
+ }
17
+ const seconds = ms / 1000;
18
+ if (seconds < 60) {
19
+ return `${seconds.toFixed(1)}s`;
20
+ }
21
+ const minutes = Math.floor(seconds / 60);
22
+ const remainingSeconds = Math.round(seconds % 60);
23
+ return `${minutes}m ${remainingSeconds}s`;
24
+ };
25
+ const formatFindingCounts = (countsBySeverity) => [
26
+ countsBySeverity.high ? `${countsBySeverity.high} high` : undefined,
27
+ countsBySeverity.warning ? `${countsBySeverity.warning} warning` : undefined,
28
+ countsBySeverity.info ? `${countsBySeverity.info} info` : undefined,
29
+ ]
30
+ .filter((part) => part !== undefined)
31
+ .join(", ");
32
+ export const formatAgentRunSummary = (params) => {
33
+ const { outputDir, run } = params;
34
+ const humanReport = params.humanReport ?? run.human_report ?? undefined;
35
+ const toAbsolute = (relativePath) => (relativePath ? join(outputDir, relativePath) : undefined);
36
+ const lines = [];
37
+ const exitCode = run.actual_exit_code ?? run.original_exit_code;
38
+ const flaky = run.summary.stats.flaky ?? 0;
39
+ const failedCount = (run.summary.stats.failed ?? 0) + (run.summary.stats.broken ?? 0);
40
+ lines.push(`Allure agent: ${formatStatusCounts(run.summary.stats)} · exit ${exitCode ?? "unknown"}` +
41
+ (params.durationMs !== undefined ? ` · ${formatDurationMs(params.durationMs)}` : "") +
42
+ (flaky ? ` · ${flaky} flaky` : ""));
43
+ if (run.check_summary.total > 0) {
44
+ lines.push(` findings: ${run.check_summary.total} (${formatFindingCounts(run.check_summary.countsBySeverity)}) — a green count is not a pass`);
45
+ }
46
+ if (run.expectations_present) {
47
+ lines.push(` expectations: ${run.expectation_result.status} (${run.expectation_result.impact})`);
48
+ }
49
+ if (run.modeling?.completeness === "partial") {
50
+ lines.push(" runtime modeling: partial — some runner failures are outside logical tests; check stderr");
51
+ }
52
+ if (params.rerunCommand && failedCount > 0) {
53
+ lines.push(` rerun failed: allure agent --rerun-from ${outputDir} --rerun-preset failed -- ${params.rerunCommand}` +
54
+ ` (reruns ${failedCount} failed test${failedCount === 1 ? "" : "s"})`);
55
+ }
56
+ lines.push("");
57
+ lines.push("Reach every conclusion from this agent output, not the raw console:");
58
+ lines.push(` overview ${toAbsolute(run.paths.index_md)} (read this first)`);
59
+ lines.push(` findings ${toAbsolute(run.paths.findings_manifest)}`);
60
+ lines.push(` tests ${toAbsolute(run.paths.tests_manifest)}`);
61
+ const logs = [toAbsolute(run.paths.process_logs.stdout), toAbsolute(run.paths.process_logs.stderr)].filter((value) => value !== undefined);
62
+ if (logs.length) {
63
+ lines.push(` test logs ${logs.join(" ")}`);
64
+ }
65
+ lines.push(` run data ${join(outputDir, "manifest", "run.json")}`);
66
+ lines.push(` guide ${toAbsolute(run.paths.agents_md)}`);
67
+ lines.push(` inspect allure agent query --from ${outputDir} summary|tests|findings|test`);
68
+ const reportPath = humanReport?.status === "generated" ? toAbsolute(humanReport.path) : undefined;
69
+ if (reportPath) {
70
+ lines.push(`Here is the report: [report](${reportPath})`);
71
+ }
72
+ return lines;
73
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/plugin-agent",
3
- "version": "3.13.2",
3
+ "version": "3.14.1",
4
4
  "description": "Allure Agent Plugin – AI-friendly markdown report generator",
5
5
  "keywords": [
6
6
  "agent",
@@ -31,12 +31,12 @@
31
31
  "lint:fix": "yarn run -T oxlint --import-plugin --fix src test features stories"
32
32
  },
33
33
  "dependencies": {
34
- "@allurereport/core-api": "3.13.2",
35
- "@allurereport/plugin-api": "3.13.2",
34
+ "@allurereport/core-api": "3.14.1",
35
+ "@allurereport/plugin-api": "3.14.1",
36
36
  "yaml": "^2.8.1"
37
37
  },
38
38
  "devDependencies": {
39
- "@allurereport/reader-api": "3.13.2",
39
+ "@allurereport/reader-api": "3.14.1",
40
40
  "@types/node": "^20",
41
41
  "@vitest/runner": "^2",
42
42
  "allure-js-commons": "^3",