feat(php): add a PHP skill, grounded in the synchra-php SDK and the bleichi site
PHP was the one language with real code in two shapes here and no skill: a Composer library held at PHPStan level max, and a plain PHP-FPM application behind nginx with no framework. The rules come from reading both rather than from general PHP advice, so each one names the failure it prevents. The two halves differ enough to be separate sections. A library requires PSR interfaces and ships no implementation; an application's rules are about escaping, keeping the document root unwritable, atomic writes and sessions. Everything in §1-§8 applies to both. Three findings are worth the space they take because they were each paid for once already: `json_encode` with JSON_UNESCAPED_SLASHES into an inline <script> is an XSS hole, a web user that cannot write its own cache fails silently on every request, and inside the dev container the host's own `php` bridge shadows the real interpreter so every vendor/bin tool dies naming distrobox. docs(python): cover MCP servers, Red cogs and HA components, and correct three rules the owner's own code contradicts The skill described a toolchain and was silent on the four things most of this Python actually is. Three of its rules are also wrong in context, and the code is right: getLogger(__name__) puts a cog outside the red.* tree where Red's log controls apply; TaskGroup's cancel-on-first-error is the wrong default for fan-out over N providers, where the real pattern is a semaphore plus gather(return_exceptions=True); and the gate runs locally and through pre-commit, not through CI, which never runs under this account. Also: ruff ALL-minus-documented-ignores is the preferred shape rather than the explicit select list, lockfiles split by artifact type, and bare `python` and bare `pip` are different interpreters on this host. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1b688d128c
commit
5aef9707d2
5 files changed
+694
-24
No files matched your search
+123
-3
@@ -4,8 +4,10 @@ description: >-
|
||||
Use this skill whenever invoking a command-line tool, choosing between a CLI and an ad-hoc
|
||||
script, writing a standalone utility script, or orienting on unfamiliar work before starting.
|
||||
Covers discovering a CLI's real interface instead of guessing flags, non-interactive and
|
||||
machine-readable invocation, script placement and symlinking, and care around destructive
|
||||
or outward-facing commands.
|
||||
machine-readable invocation, checking an API's payload rather than its exit status, keeping
|
||||
credentials out of argv, eventually-consistent systems that briefly report the old state,
|
||||
script placement and symlinking, and care around destructive or outward-facing commands —
|
||||
including additive changes that silently take over a name already serving traffic.
|
||||
---
|
||||
|
||||
# CLI & Tooling — Discovery, Invocation, and Script Placement
|
||||
@@ -83,6 +85,16 @@ tool list -o csv # structured columns for a spreadsheet or script
|
||||
|
||||
- **Never scrape a table meant for humans.** Column widths, truncation, colour codes, and
|
||||
alignment all change without notice; a structured format is a contract.
|
||||
- **Check the payload, not just the exit status.** Plenty of HTTP APIs answer `200 OK` with
|
||||
`{"success": false, "errors": [...]}` in the body, and `curl` exits `0` because the
|
||||
*request* worked. A script that trusts the exit code reports a clean success and writes
|
||||
nothing. Assert on the field the API uses for this, and print `errors[]` when it trips:
|
||||
```bash
|
||||
if [[ "$(jq -r '.success' <<<"$response")" != true ]]; then
|
||||
jq -r '.errors[]? | " - \(.message)"' <<<"$response" >&2
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
- Strip colour explicitly (`--color never`, `NO_COLOR=1`) when capturing output, or escape
|
||||
sequences end up in your parsed values.
|
||||
- Keep in mind that a "compact" or summary format is the right default for feeding an agent,
|
||||
@@ -166,9 +178,117 @@ system, not scattered at the repository root.
|
||||
- Anything that sends, publishes, deploys, or purchases is outward-facing: confirm first, and
|
||||
treat approval as applying to that one action rather than the category.
|
||||
|
||||
### Adding something can take something over
|
||||
|
||||
> [!CAUTION]
|
||||
> **A change that deletes nothing can still break a live service.** The dangerous operations
|
||||
> are not only the ones named `delete`. Adding a routing rule, a proxy entry, a redirect, a
|
||||
> DNS override or a higher-priority config can silently intercept a name that is already
|
||||
> serving traffic — while the thing it shadows still looks perfectly healthy in every
|
||||
> listing, because it was never touched.
|
||||
>
|
||||
> That is the worst shape a failure can take: the old config is still there, still correct,
|
||||
> still pointing where it should, and the service is dead anyway. Nothing in the inventory
|
||||
> says what happened.
|
||||
|
||||
**Before claiming a name, ask what currently answers to it** — and refuse by default when
|
||||
something does:
|
||||
|
||||
```bash
|
||||
# Only an unrouteable placeholder, or an entry this tool made, is safe to assume is free.
|
||||
if [[ "$(current_target "$host")" != "$PLACEHOLDER" && "$(owner "$host")" != "$MARKER" ]]; then
|
||||
[[ "$FORCE" == true ]] || die "$host already serves $(current_target "$host") — pass --force"
|
||||
fi
|
||||
```
|
||||
|
||||
Two things make that check possible:
|
||||
|
||||
- **Mark what you create.** A comment field, a tag, a description prefix — anything that
|
||||
distinguishes *your* entries from ones a human made. It turns "is this mine to replace?"
|
||||
into a lookup rather than a guess, and it is what lets the matching `--delete` refuse to
|
||||
remove something it did not create.
|
||||
- **Make `--force` the only way past**, and name in the refusal what is there now. A tool that
|
||||
silently overwrites is one nobody can safely run against production.
|
||||
|
||||
> [!TIP]
|
||||
> Test such a tool against a name you **expect** it to reject, not only against a free one.
|
||||
> The rejection path is where the damage would have been, and it is the half a happy-path test
|
||||
> never exercises.
|
||||
|
||||
---
|
||||
|
||||
## 7. Environment Constraints
|
||||
## 7. Credentials Belong on stdin, Not in `argv`
|
||||
|
||||
> [!WARNING]
|
||||
> **`argv` is world-readable.** Any user on the box can run `ps` and read the full command
|
||||
> line of every process. A token passed as `-H "Authorization: Bearer $TOKEN"`, `--password`,
|
||||
> or `--token` is therefore exposed for as long as the call runs — and lands in shell history
|
||||
> and process accounting too.
|
||||
|
||||
Most CLIs that accept a secret as a flag also accept it somewhere safer. Use that instead:
|
||||
|
||||
```bash
|
||||
# WRONG — the token is visible in `ps` for the life of the request
|
||||
curl -H "Authorization: Bearer $TOKEN" "$url"
|
||||
|
||||
# RIGHT — curl reads its options from stdin; argv holds nothing sensitive
|
||||
printf 'header = "Authorization: Bearer %s"\n' "$TOKEN" | curl --config - "$url"
|
||||
```
|
||||
|
||||
The equivalent elsewhere: `--password-stdin` (docker login, many package tools), `-u` with a
|
||||
`.netrc`, `--token-file`, or simply an environment variable the tool reads by name. Reach for
|
||||
the env var when nothing better exists — it is readable through `/proc/<pid>/environ` but only
|
||||
by the owner and root, which is a meaningfully smaller exposure than `argv`.
|
||||
|
||||
See [`code-languages/bash.md` §8](skills-public/code-languages/bash.md) for the surrounding rules on
|
||||
where secrets are stored and why they are never echoed.
|
||||
|
||||
---
|
||||
|
||||
## 8. Eventually-Consistent Systems Lie Briefly
|
||||
|
||||
Edge networks, DNS, CDNs, object stores, container registries and clustered schedulers all
|
||||
acknowledge a write before it is visible everywhere. **A read taken immediately after a
|
||||
successful write is not evidence of anything.**
|
||||
|
||||
The failure mode is believing the tool is broken and "fixing" it:
|
||||
|
||||
- A changed rule still returns the **old** value for a while — which reads as "my write was
|
||||
ignored".
|
||||
- A brand-new name returns an **error** for a while — a 5xx, an NXDOMAIN, a 404 — which reads
|
||||
much worse than it is.
|
||||
|
||||
So: take the write's own success response as the result, and treat the first few reads as
|
||||
unreliable. When a verification read matters, **say how long propagation takes** in the tool's
|
||||
own help or header comment, so the next person does not rediscover it at 2am. State it as a
|
||||
number — "~30s for a rule, ~40s for a new name" — because "eventually" tells nobody when to
|
||||
start worrying.
|
||||
|
||||
Do not wrap this in a polling loop (see
|
||||
[`code-languages/bash.md` §3](skills-public/code-languages/bash.md)); take one sample per
|
||||
invocation and let the caller run it again.
|
||||
|
||||
> [!WARNING]
|
||||
> **Checking too early can make the delay much worse.** A failed lookup is itself cached. Query
|
||||
> a DNS name before it exists and the resolver stores the `NXDOMAIN` for the SOA's negative TTL
|
||||
> — commonly 300-1800s — so the name stays invisible to *you* long after it is live for
|
||||
> everyone else. Polling during propagation is what creates this, which is a second reason not
|
||||
> to loop.
|
||||
>
|
||||
> When a check does come back negative, confirm where the negative lives before concluding
|
||||
> anything: query the authoritative source directly and compare. The answer is often "the
|
||||
> record is fine, my resolver is lying", and flushing the local cache will not help if the
|
||||
> stale entry is on an upstream resolver you do not control.
|
||||
>
|
||||
> ```bash
|
||||
> dig +short @1.1.1.1 "$name" # authoritative-ish: is it actually published?
|
||||
> dig +short "$name" # what my resolver believes
|
||||
> curl --resolve "$name:443:<ip>" "https://$name/" # bypass DNS entirely to test the service
|
||||
> ```
|
||||
|
||||
---
|
||||
|
||||
## 9. Environment Constraints
|
||||
|
||||
- **Entering a container or changing shells resets the working directory.** Chain `cd` inside
|
||||
the same command string rather than relying on it persisting:
|
||||
|
||||
Reference in new issue
Block a user