Skip to content

Commit adb8f61

Browse files
[tests] Structure agent-driven PHPUnit JSON results (#329)
* [tests] Structure agent-driven PHPUnit JSON results * Update wiki submodule pointer for PR #329 * [tests] Wrap structured PHPUnit output in command JSON * [tests] Suppress extra JSON progress records * [console] Decode nested structured command output * Update wiki submodule pointer for PR #329 * [console] Suppress structured progress preambles * [console] Normalize Rector changed file lists * [bootstrap] Normalize CLI autoload bootstrap * [console] Ignore warning preambles before structured JSON * [console] Centralize implicit JSON output detection * [container] Centralize trait-only service resolution * Update wiki submodule pointer for PR #329 * [console] Align structured output detection * Update wiki submodule pointer for PR #329 * [console] Normalize structured command output * [tests] Stabilize structured reporter expectations * Update wiki submodule pointer for PR #329 * [console] Centralize non-terminal command logging * [container] Remove redundant DevTools accessors * Update wiki submodule pointer for PR #329 * [console] Remove intermediateInfo helper * [console] Remove unused notice helper * Refine structured command logging * Update wiki submodule pointer for PR #329 * Fix coverage metadata for command provider test * Preserve structured PHPUnit raw output * Fix coverage metadata for command tests * Simplify structured command logging * Remove redundant service provider alias * Update wiki submodule pointer for PR #329 * Use DevTools test double in command provider test * Use PHPUnit lifecycle attributes for container reset * Refresh README and testing docs * Refresh changelog entry for structured output work --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
1 parent a97abb2 commit adb8f61

92 files changed

Lines changed: 2705 additions & 651 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/wiki

Submodule wiki updated from 19c36c7 to 989c09c

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
- Keep structured DevTools command output parseable and agent-friendly by exposing the PHPUnit agent-reporter payload under nested `output`, suppressing intermediary progress logs in JSON modes, and normalizing orchestrated subprocess payloads before the final JSON envelope is emitted (#248)
1213
- Register ``ergebnis/phpunit-agent-reporter`` in the packaged ``phpunit.xml`` so AI agents receive compact PHPUnit JSON summaries without changing consumer overrides manually (#327)
1314
- Let `wiki`, `docs`, `tests`, `metrics`, and `reports` skip gracefully for guide-only repositories while keeping wiki/report workflows and published preview links aligned with the artifacts that were actually generated (#325)
1415

README.md

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ across Fast Forward libraries.
3535
- Synchronizes packaged skills and project-agent prompts into consumer
3636
`.agents/skills` and `.agents/agents` directories using safe link-based
3737
updates
38+
- Supports guide-only and automation-only repositories by skipping PHPUnit or
39+
wiki generation gracefully when no runnable PHP surface exists
3840
- Works both as a Composer plugin and as a local binary
3941
- Preserves local overrides through consumer-first configuration resolution
4042

@@ -71,6 +73,8 @@ You can also run individual commands for specific development tasks:
7173
```bash
7274
# Run PHPUnit tests
7375
composer dev-tools tests
76+
composer dev-tools tests --json
77+
composer dev-tools tests --pretty-json
7478

7579
# Analyze missing, unused, misplaced, and outdated Composer dependencies
7680
composer dependencies
@@ -120,6 +124,7 @@ composer phpdoc
120124

121125
# Generate HTML API documentation using phpDocumentor
122126
composer docs
127+
composer docs --source=docs/user-guide
123128

124129
# Generate Markdown documentation for the wiki
125130
composer wiki
@@ -180,6 +185,14 @@ The `metrics` command ships with `phpmetrics/phpmetrics` as a direct
180185
dependency of `fast-forward/dev-tools`, so consumer repositories can generate
181186
metrics reports without extra setup.
182187

188+
Guide-only repositories and workflow-only repositories can still use the
189+
packaged command surface. When no runnable PHPUnit surface exists, `tests`
190+
returns a controlled warning instead of failing. When a repository ships
191+
guides without PSR-4 source paths, `docs` builds the guide site without trying
192+
to synthesize API pages. When `.github/wiki` is absent and no wiki has been
193+
initialized yet, `wiki` now skips generation with a warning instead of failing
194+
the whole automation run.
195+
183196
The changelog commands manage Keep a Changelog 1.1.0 files without requiring
184197
extra tooling in the consumer repository. `changelog:entry` bootstraps a
185198
missing changelog file on first use, `changelog:check` enforces meaningful
@@ -209,16 +222,35 @@ automatically when the runtime is detected as agent-driven. For
209222
prints raw values so release workflows can keep capturing semantic versions
210223
and piping rendered release notes directly into GitHub releases.
211224

225+
`--pretty-json` intentionally remains valid JSON. DevTools does not inject ANSI
226+
escape sequences into that mode today because preserving a parseable payload
227+
takes precedence over terminal-only color. Where orchestrated tools can expose
228+
structured subprocess results safely, DevTools prefers adding stable fields to
229+
the JSON context rather than coloring otherwise strict JSON output.
230+
231+
For the `tests` command, structured runs now capture the bundled PHPUnit
232+
agent-reporter payload inside `context.output` while preserving the normal
233+
DevTools JSON envelope. That means the top-level command still emits one final
234+
document, and consumers can inspect stable nested keys such as
235+
`context.output.result`, `context.output.summary`, optional
236+
`context.output.details`, and `context.output.coverage` when minimum-coverage
237+
validation is active.
238+
212239
Progress output is disabled by default on the commands that support transient
213240
rendering, and `--progress` re-enables it for human-readable terminal runs.
214241
When `--json` or `--pretty-json` is active on commands that orchestrate other
215242
tools, DevTools keeps progress suppressed, forwards JSON flags where the
216243
underlying tool supports structured output, and otherwise falls back to
217-
quieter subprocess modes so the captured payload stays machine-readable. In
218-
GitHub Actions, queued subprocess output is grouped into collapsible sections,
219-
and logged failures emit native workflow error annotations, including file and
220-
line metadata when commands provide it. The packaged tests, reports, wiki, and
221-
changelog workflows also append concise Markdown outcomes to
244+
quieter subprocess modes so the captured payload stays machine-readable. The
245+
`tests` command now captures the bundled PHPUnit agent-reporter payload in
246+
structured runs and stores it in `context.output`, preserving `result`,
247+
`summary`, optional `details`, and a `raw_output` fallback when coverage or
248+
other PHPUnit text is emitted before the final reporter JSON. In GitHub
249+
Actions, queued subprocess output is
250+
grouped into collapsible sections, and logged failures emit native workflow
251+
error annotations, including file and line metadata when commands provide it.
252+
The packaged tests, reports, wiki, and changelog workflows also append concise
253+
Markdown outcomes to
222254
`GITHUB_STEP_SUMMARY` so maintainers can scan versions, URLs, preview refs,
223255
verification status, and release results without expanding full logs. This
224256
repository also keeps a bounded retry workflow that reruns failed jobs once
@@ -324,8 +356,8 @@ authoring.
324356
- `Composer Plugin` - `FastForward\DevTools\Composer\Plugin` exposes the
325357
packaged command set to Composer and runs `dev-tools:sync` after install and
326358
update.
327-
- `DevTools Container` - `FastForward\DevTools\Console\DevTools::create()`
328-
builds a shared container from `DevToolsServiceProvider`, which wires
359+
- `DevTools Container` - `FastForward\DevTools\Container\ContainerFactory::get(FastForward\DevTools\Console\DevTools::class)`
360+
resolves the shared application from `DevToolsServiceProvider`, which wires
329361
process execution, filesystem access, changelog services, Git helpers,
330362
diffing, reporting, and template loading.
331363
- `Generic Link Synchronization` -

bin/dev-tools.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,18 @@
2020
namespace FastForward\DevTools;
2121

2222
use FastForward\DevTools\Console\DevTools;
23+
use FastForward\DevTools\Container\ContainerFactory;
2324

24-
$projectVendorAutoload = \dirname(__DIR__, 4) . '/vendor/autoload.php';
25-
$pluginVendorAutoload = \dirname(__DIR__) . '/vendor/autoload.php';
25+
$autoloadCandidates = [\dirname(__DIR__, 4) . '/vendor/autoload.php', \dirname(__DIR__) . '/vendor/autoload.php'];
2626

27-
require_once file_exists($projectVendorAutoload) ? $projectVendorAutoload : $pluginVendorAutoload;
27+
foreach ($autoloadCandidates as $autoloadCandidate) {
28+
if (is_file($autoloadCandidate)) {
29+
require_once $autoloadCandidate;
2830

29-
DevTools::create()->run();
31+
exit(ContainerFactory::get(DevTools::class)->run());
32+
}
33+
}
34+
35+
fprintf(\STDERR, "Could not locate Composer autoload.php for fast-forward/dev-tools.\n");
36+
37+
exit(1);

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@
4444
"container-interop/service-provider": "^0.4.1",
4545
"dg/bypass-finals": "^1.9",
4646
"ergebnis/agent-detector": "^1.1",
47-
"ergebnis/phpunit-agent-reporter": "^0.3",
4847
"ergebnis/composer-normalize": "^2.51",
48+
"ergebnis/phpunit-agent-reporter": "^0.3",
4949
"ergebnis/rector-rules": "^1.18",
5050
"fakerphp/faker": "^1.24",
5151
"fast-forward/phpdoc-bootstrap-template": "^2.0",

docs/advanced/phpunit-extension.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ Runtime Chain
1919
builds a summary notification and sends it when the run finishes.
2020
5. ``Ergebnis\PHPUnit\AgentReporter\Extension`` replaces PHPUnit's normal
2121
output with a compact JSON report when an agent runtime is detected.
22+
6. In structured DevTools runs, ``tests`` forces the same reporter path for
23+
the PHPUnit subprocess so the final nested payload remains deterministic
24+
even when the surrounding process would not naturally look agent-driven.
2225

2326
Why This Helps Consumer Projects
2427
--------------------------------
@@ -29,6 +32,8 @@ Why This Helps Consumer Projects
2932
scrollback;
3033
- agent-driven runs consume far less terminal context while still keeping
3134
failure details and PHPUnit exit semantics intact;
35+
- DevTools can preserve a single top-level JSON document while nesting the
36+
compact PHPUnit summary under ``context.output`` for bot-friendly parsing;
3237
- event counts are available to the notification layer without adding ad-hoc
3338
test code.
3439

docs/api/composer-integration.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ Startup Chain
1212
``vendor/autoload.php`` and falls back to the package autoloader.
1313
3. ``bin/dev-tools.php`` starts ``FastForward\DevTools\Console\DevTools`` and appends
1414
``--no-plugins``.
15-
4. ``FastForward\DevTools\Console\DevTools::create()`` builds the shared
16-
container from ``FastForward\DevTools\ServiceProvider\DevToolsServiceProvider``.
15+
4. ``FastForward\DevTools\Container\ContainerFactory::get(FastForward\DevTools\Console\DevTools::class)``
16+
resolves the shared application container from
17+
``FastForward\DevTools\ServiceProvider\DevToolsServiceProvider``.
1718
5. ``FastForward\DevTools\Console\CommandLoader\DevToolsCommandLoader``
1819
lazily discovers ``#[AsCommand]`` classes from the command namespace.
1920
6. ``FastForward\DevTools\Composer\Capability\DevToolsCommandProvider`` later

docs/commands/docs.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,14 @@ Exit Codes
103103
Behavior
104104
---------
105105

106-
- ``docs/`` must exist unless you pass another ``--source`` directory.
106+
- The default ``docs`` source directory is optional. When it is absent, the
107+
command still generates API pages if PSR-4 source paths are available.
108+
- A custom explicit ``--source`` path must exist; otherwise the command fails
109+
fast.
107110
- API pages are built from the PSR-4 paths declared in ``composer.json``.
108111
- Guide pages are built from the selected source directory.
112+
- Repositories without PSR-4 source paths can still generate a guides-only
113+
site from the selected source directory.
109114
- Cache stays enabled by default; omit both flags to keep the command default,
110115
pass ``--cache`` to force it on, and pass ``--no-cache`` to force it off.
111116
- When ``--cache-dir`` is omitted, phpDocumentor keeps its default cache

docs/commands/reports.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ Behavior
123123
- When ``--json`` or ``--pretty-json`` is active, it forwards JSON mode to the
124124
``docs``, ``tests``, and ``metrics`` subprocesses and suppresses transient
125125
progress output where those tools support it.
126+
- Nested ``docs`` and ``tests`` stages can now skip gracefully with warnings in
127+
guide-only or automation-only repositories, while ``reports`` still returns a
128+
successful aggregate result for the stages that were actually generated.
126129
- Passes ``--junit <coverage>/junit.xml`` to the metrics step.
127130
- Used by the ``standards`` command as the final phase.
128131
- This is the reporting stage used by GitHub Pages.

docs/commands/tests.rst

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ Options
7171
``--pretty-json``
7272
Emit the same structured payload with indentation for terminal inspection.
7373
This also suppresses PHPUnit progress output automatically so the JSON
74-
payload is not polluted by transient progress rendering.
74+
payload is not polluted by transient progress rendering. The output remains
75+
valid JSON and intentionally does not include ANSI color escapes.
7576

7677
Examples
7778
--------
@@ -112,6 +113,13 @@ Run with minimum coverage enforcement:
112113
113114
composer tests --min-coverage=80
114115
116+
Run with structured JSON output:
117+
118+
.. code-block:: bash
119+
120+
composer tests --json
121+
composer tests --pretty-json
122+
115123
Run without cache:
116124

117125
.. code-block:: bash
@@ -149,6 +157,10 @@ Behavior
149157
---------
150158

151159
- Local ``phpunit.xml`` is preferred over the packaged default.
160+
- When the default ``tests`` path is absent and the project exposes no
161+
testable PHP source files, the command exits successfully with a warning
162+
instead of failing the whole automation flow.
163+
- A custom explicit tests path that does not exist still fails fast.
152164
- Coverage filters are automatically applied to all PSR-4 paths from composer.json.
153165
- Multiple coverage formats are generated: HTML, Testdox HTML, Clover XML, and PHP.
154166
- Cache stays enabled by default; omit both flags to keep the command default,
@@ -160,5 +172,25 @@ Behavior
160172
- progress output is disabled by default.
161173
- ``--json`` and ``--pretty-json`` keep progress output disabled so the
162174
structured payload stays clean, even when ``--progress`` is provided.
175+
- in agent-driven runs outside the Composer test suite, the command also
176+
switches to the same structured capture mode automatically.
177+
- when structured capture is active and PHPUnit emits agent-reporter JSON, the
178+
command stores that payload inside ``output`` while keeping the standard
179+
DevTools JSON envelope. ``--json`` and ``--pretty-json`` therefore expose
180+
the same structured result, with formatting as the only difference.
181+
- the command forces the bundled PHPUnit agent-reporter extension for
182+
structured runs so the nested payload stays compact and stable even when the
183+
surrounding runtime would not normally look like an agent.
184+
- in structured mode, the command suppresses intermediary ``Running...`` log
185+
records so the output stream contains a single final JSON document.
186+
- when structured capture is active but PHPUnit does not emit parseable JSON,
187+
the command preserves the raw subprocess text inside ``output.raw_output``
188+
instead of dropping it.
189+
- when coverage generation or other PHPUnit text appears before the final
190+
reporter payload, the command preserves that prelude in
191+
``output.raw_output`` while keeping the main JSON result parseable.
192+
- when ``--min-coverage`` is used in structured mode, the command appends a
193+
``coverage`` object under ``output`` and flips ``output.result`` to
194+
``failure`` if the threshold is not met.
163195
- The command fails if minimum coverage is not met (when ``--min-coverage`` is set).
164196
- The packaged configuration registers the DevTools PHPUnit extension.

0 commit comments

Comments
 (0)