A package audit tool scored Atelier at 92. The web page listed three failures and a warning about files in the release archive. The JSON API told a different story: one check failed, worth three points, and the archive was clean. Fixing it took one file, and adding CI afterwards was the part that could have cost points.
v0.1.3 has no package code in it. It is a security policy, CI, and a dependency updater, and it exists because an audit tool gave me a number and I went looking for the reasoning behind it.
The tool was Plumb, which scores PHP packages on security, maintenance and ecosystem signals. Atelier came out at 92 out of 100.
The rendered summary was misleading
Read as a web page, the report said three security checks failed and flagged "4 items" as development files shipped inside the release archive.
That would have been a morning of work. It was also wrong.
The same tool has a JSON API, and it disagreed with its own rendering:
| Check | Weight | Status |
|---|---|---|
| Open security advisories | 10 | pass |
| Dependabot PR responsiveness | 7 | pass |
| Security policy present | 3 | fail |
| Actions pinned to SHA | 8 | not applicable |
| Dependabot or Renovate configured | 5 | not applicable |
| Dependency update cooldown | 4 | not applicable |
One failure. Seventeen of twenty applicable weight, which is exactly the 85 the security category showed.
The "4 items" in the archive was not four files. It was the four category names the check looks in, and the evidence field read {"ai": [], "ci": [], "tests": [], "tooling": []}. All empty. The check passed. I confirmed it independently by downloading the actual release zip and listing it, which is the sort of thing worth doing before believing either the tool or myself.
So the entire gap was one missing file.
The file was worth writing properly
SECURITY.md could be four lines. It is more useful as a scope document, because the interesting question for this package is what counts as a vulnerability at all.
In scope: template injection through block content, draft content reaching an unauthenticated visitor, a preview link that resolves without a valid signature, slug handling that serves a page the request did not ask for.
Out of scope, stated explicitly: the raw HTML block executing the HTML somebody deliberately typed into it. That is the feature. Without saying so, every security researcher who finds it reports it, and every report costs a reply.
I also turned on GitHub's private vulnerability reporting so the link in the policy resolves to something rather than being a gesture.
Adding CI was the part that could have gone backwards
Three of those checks were "not applicable" for one reason: there was no .github/workflows directory. Between them they carry 17 weight, dormant.
The moment you add a single workflow, all three activate. Every third-party action then needs a full 40-character commit SHA, a dependency updater has to cover the github-actions ecosystem, and that updater needs a cooldown configured. Done carelessly, adding CI would have dropped the score from 100 to around 75.
That is an argument for adding CI deliberately, not for avoiding it. The suite had 29 tests that only ran when I remembered to run them.
Actions are pinned like this, with the version in a comment because a SHA tells a reader nothing:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
The reason is not theoretical. In March 2025 a widely used action had its tag re-pointed at malicious code and CI secrets were exfiltrated from thousands of repositories. A tag is a mutable pointer somebody else controls.
Dependabot covers the workflows on a seven day cooldown, because merging an update the moment it is published is precisely the window a malicious release depends on.
The floor I was claiming and not testing
The suite runs on PHP 8.4. It cannot run on 8.3, because Pest 5 requires 8.4.
composer.json claims ^8.3. Dropping 8.3 from the matrix silently would have left a public promise that nothing verified.
So there is a separate job that checks the two halves that promise is actually made of: that the package's own code parses under 8.3, and that a consumer on 8.3 can resolve and install it. Both pass. The claim is honest, and now it is checked rather than asserted.
The bug the dry run caught
I ran the workflow's commands in a clean clone before pushing. php artisan test from the repository root failed with Could not open input file: vendor/pestphp/pest/bin/pest, because artisan test spawns Pest at a path relative to the current directory.
Ten seconds to fix, and it would otherwise have been a red first build that looks like the feature is broken rather than the pipeline.
Where it stands
v0.1.3. A security policy, CI on every push, a dependency updater, and a changelog covering every release back to the first.
The composite score goes to 100 once the tool rescans, which it does daily. Which matters less than the two things that came out of it: the tests now run whether or not I remember, and there is a documented place to report something that should not be a public issue.