Compare commits

..
Author SHA1 Message Date
Michael SitarzewskiandClaude Opus 5.5 c078bd0db4 chore(eval): manifest lines for the 35 content fixes
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 22:12:25 -05:00
Michael Sitarzewski f16a99f47d Merge commit 'refs/c35/953' into integ/content-batch 2026-09-30 21:38:27 -05:00
Michael Sitarzewski 6ab420d0b4 Merge commit 'refs/c35/952' into integ/content-batch 2026-09-30 21:38:27 -05:00
Michael Sitarzewski f0402f8c14 Merge commit 'refs/c35/951' into integ/content-batch 2026-09-30 21:38:27 -05:00
Michael Sitarzewski 7edcb03234 Merge commit 'refs/c35/950' into integ/content-batch 2026-09-30 21:38:27 -05:00
Michael Sitarzewski c01335aa94 Merge commit 'refs/c35/949' into integ/content-batch 2026-09-30 21:38:27 -05:00
Michael Sitarzewski 068cafcce5 Merge commit 'refs/c35/948' into integ/content-batch 2026-09-30 21:38:27 -05:00
Michael Sitarzewski 8cc075bc83 Merge commit 'refs/c35/947' into integ/content-batch 2026-09-30 21:38:27 -05:00
Michael Sitarzewski 90b09628e1 Merge commit 'refs/c35/946' into integ/content-batch 2026-09-30 21:38:27 -05:00
Michael Sitarzewski 0051d7766d Merge commit 'refs/c35/945' into integ/content-batch 2026-09-30 21:38:27 -05:00
Michael Sitarzewski b36f739d3a Merge commit 'refs/c35/944' into integ/content-batch 2026-09-30 21:38:27 -05:00
Michael Sitarzewski f7dd810def Merge commit 'refs/c35/943' into integ/content-batch 2026-09-30 21:38:26 -05:00
Michael Sitarzewski 2b9b2f1578 Merge commit 'refs/c35/942' into integ/content-batch 2026-09-30 21:38:26 -05:00
Michael Sitarzewski abc87a63ba Merge commit 'refs/c35/941' into integ/content-batch 2026-09-30 21:38:26 -05:00
Michael Sitarzewski f1e5d856ce Merge commit 'refs/c35/940' into integ/content-batch 2026-09-30 21:38:26 -05:00
Michael Sitarzewski 8594e1dd73 Merge commit 'refs/c35/939' into integ/content-batch 2026-09-30 21:38:26 -05:00
Michael Sitarzewski f14ab6ff7f Merge commit 'refs/c35/938' into integ/content-batch 2026-09-30 21:38:26 -05:00
Michael Sitarzewski 161de0779b Merge commit 'refs/c35/937' into integ/content-batch 2026-09-30 21:38:26 -05:00
Michael Sitarzewski db7b18e18f Merge commit 'refs/c35/936' into integ/content-batch 2026-09-30 21:38:26 -05:00
Michael Sitarzewski b929f1e521 Merge commit 'refs/c35/935' into integ/content-batch 2026-09-30 21:38:26 -05:00
Michael Sitarzewski e402360995 Merge commit 'refs/c35/934' into integ/content-batch 2026-09-30 21:38:26 -05:00
Michael Sitarzewski bbf1c7dec3 Merge commit 'refs/c35/933' into integ/content-batch 2026-09-30 21:38:26 -05:00
Michael Sitarzewski 26678ec1c4 Merge commit 'refs/c35/932' into integ/content-batch 2026-09-30 21:38:26 -05:00
Michael Sitarzewski bfec2278e7 Merge commit 'refs/c35/931' into integ/content-batch 2026-09-30 21:38:25 -05:00
Michael Sitarzewski 72c52affeb Merge commit 'refs/c35/930' into integ/content-batch 2026-09-30 21:38:25 -05:00
Michael Sitarzewski c1b4232c67 Merge commit 'refs/c35/929' into integ/content-batch 2026-09-30 21:38:25 -05:00
Michael Sitarzewski 1462546714 Merge commit 'refs/c35/928' into integ/content-batch 2026-09-30 21:38:25 -05:00
Michael Sitarzewski b5c599dfdd Merge commit 'refs/c35/927' into integ/content-batch 2026-09-30 21:38:25 -05:00
Michael Sitarzewski 385b3bb60a Merge commit 'refs/c35/926' into integ/content-batch 2026-09-30 21:38:25 -05:00
Michael Sitarzewski ca1ef694ea Merge commit 'refs/c35/925' into integ/content-batch 2026-09-30 21:38:25 -05:00
Michael Sitarzewski 53ca655ad3 Merge commit 'refs/c35/924' into integ/content-batch 2026-09-30 21:38:25 -05:00
Michael Sitarzewski 22148bee9c Merge commit 'refs/c35/923' into integ/content-batch 2026-09-30 21:38:25 -05:00
Michael Sitarzewski 9daffc0202 Merge commit 'refs/c35/922' into integ/content-batch 2026-09-30 21:38:25 -05:00
Michael Sitarzewski f22a39c221 Merge commit 'refs/c35/921' into integ/content-batch 2026-09-30 21:38:25 -05:00
Michael Sitarzewski 3023be035f Merge commit 'refs/c35/920' into integ/content-batch 2026-09-30 21:38:24 -05:00
Michael Sitarzewski 83336b4c0a Merge commit 'refs/c35/919' into integ/content-batch 2026-09-30 21:38:24 -05:00
Michael SitarzewskiandClaude Opus 5.5 b94b72e780 chore(eval): regenerate the convert-outputs manifest for the installer batch
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 21:31:57 -05:00
Michael Sitarzewski f96c36ff3e Merge commit 'refs/b3/961' into integ/installer-batch3
# Conflicts:
#	.github/workflows/test-install.yml
2026-09-30 21:20:58 -05:00
Michael Sitarzewski 83452ee559 Merge commit 'refs/b3/956' into integ/installer-batch3
# Conflicts:
#	.github/workflows/check-tools.yml
2026-09-30 21:20:46 -05:00
Michael Sitarzewski 057243b089 Merge commit 'refs/b3/958' into integ/installer-batch3
# Conflicts:
#	.github/workflows/check-tools.yml
2026-09-30 21:20:45 -05:00
Michael Sitarzewski d85c598ce7 Merge commit 'refs/b3/960' into integ/installer-batch3 2026-09-30 21:20:37 -05:00
Michael Sitarzewski a518866195 Merge commit 'refs/b3/955' into integ/installer-batch3 2026-09-30 21:20:37 -05:00
Michael Sitarzewski 5eacda1c7c Merge commit 'refs/b3/957' into integ/installer-batch3 2026-09-30 21:20:37 -05:00
Michael Sitarzewski ee241d4b70 Merge commit 'refs/b3/962' into integ/installer-batch3 2026-09-30 21:20:37 -05:00
Michael Sitarzewski ffdf7730bb Merge commit 'refs/b3/954' into integ/installer-batch3 2026-09-30 21:20:37 -05:00
Rudy Celekli 947ba568e9 fix(install): replay failed parallel workers before returning
Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>
2026-09-30 21:42:28 -04:00
Rudy Celekli 266c0864fe fix(lint): reject unsupported folded YAML frontmatter
Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>
2026-09-30 21:38:48 -04:00
Rudy Celekli 3711d8729a fix(install): preserve user files and foreign links in link mode
Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>
2026-09-30 21:38:47 -04:00
Rudy Celekli e9a1926f35 docs(identity): preserve caller privileges through database objects
Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>
2026-09-30 21:25:12 -04:00
Rudy Celekli 39bb5e4c96 docs(payments): clarify inbox claims and event ordering
Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>
2026-09-30 21:25:11 -04:00
Rudy Celekli b34a2af73b fix(dbre): keep legacy updates working during backfill
Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>
2026-09-30 21:25:09 -04:00
hotragn d9e8a51ea2 test(install): the spaced-path --parallel exit was passing for the wrong reason
With a spaced --path, the parallel workers get the path split into words
and reject the stray words as unknown options (the bug #755 fixes). Unknown
options used to exit 0, so every worker "succeeded" having installed
nothing, and this case's exit-code assert passed while the count assert
next to it was already an xfail for #755.

Now that unknown options exit 1, the parent exits non-zero there, which is
the truth. The exit-code assert becomes an xfail for #755 like its
neighbour; both will report "appears to have landed" once #755 merges.
2026-09-30 05:21:11 -04:00
hotragn bdb3143fcd fix(cli): an unknown option exits 1; convert.sh --help shows every option
Both scripts handled an unknown option with `error ...; usage`, and usage()
always ended in `exit 0`:

  $ ./scripts/convert.sh --tol codex; echo $?
  [ERR] Unknown option: --tol
  ...usage...
  0

so a mistyped flag in CI or a wrapper script read as success. usage() now
takes a status. --help still prints to stdout and exits 0; an unknown
option prints the usage text to stderr and exits 1, the same as a bad
--tool or --division value already does.

convert.sh --help also printed a hard-coded `sed -n '3,28p'`. The option
notes live on lines 33-34, so --parallel and --jobs never appeared, and
--out was only in the synopsis. It now uses USAGE-START/END sentinels, as
install.sh has for a while, and the option list covers --tool and --out.

test-cli-usage.sh checks both scripts: an unknown option exits non-zero
with its name and the usage on stderr and nothing on stdout, --help exits 0
without printing the sentinels, and convert.sh --help describes --tool,
--out, --parallel and --jobs. It fails on main at the first check and runs
in check-tools.yml.
2026-09-30 05:02:50 -04:00
hotragn 33f68d61b7 fix(install): accept the file-stem ids runbooks.json uses for --agent
strategy/runbooks.json names agents by file stem, the corpus id the desktop
app maps to catalog entries ("engineering-frontend-developer"). The installer
only accepted the slug built from `name:` ("frontend-developer"), and for
206 of the 279 agents those differ. Of the 36 agents the four runbooks list,
35 could not be selected by the ids the runbooks give them:

  $ install.sh --tool claude-code --agent engineering-frontend-developer --dry-run
  [ERR] Unknown agent 'engineering-frontend-developer'. Use --list agents ...

So there was no way to install a runbook's team from the CLI without
translating every id by hand.

resolve_agent now takes a slug, a display name (as before, via slugify), or a
file stem, and returns the install slug. Slugs are tried first. No stem
equals a different agent's slug today, and stems are unique, so nothing that
resolved before resolves differently now.

Resolution also stops rescanning the roster per request. agent_slug_exists
ran get_field over all 279 files for each requested agent; roster_index
builds the slug/stem table once in the parent shell. A 36-line agents file
went from ~10,000 get_field calls to 279.

test-agent-selection.sh gains a stem case and feeds each runbook's roster to
--agents-file, checking that it resolves to exactly its own agent count. It
fails on main at the first runbook. The usage text and the README selection
section say which ids are accepted, with an example of installing a
runbook's team.
2026-09-30 04:55:57 -04:00
hotragn ec4ff9fed4 fix(install): one failing tool no longer stops the tools after it
A sequential install called install_tool bare under set -e, so the first
tool that returned 1 exited the whole script. With copilot, cursor, qwen,
codex and hermes detected and no integrations/cursor:

  [1/5] copilot   [OK]
  [2/5] cursor    [ERR] integrations/cursor missing. Run convert.sh first.
  (exit 1)

qwen, codex and hermes were never attempted, and nothing said so. The same
happens for any ensure_converted failure or an OpenClaw registration error.

Each tool now runs in a subshell with errexit on. It is not
`install_tool "$t" || failed+=(...)`: bash ignores errexit for everything on
the left of ||, subshells included, so a failing cp inside a tool would carry
on as if it had worked. Instead the parent turns errexit off for that one
command, and the subshell turns it back on for itself.

A failed tool is recorded and the loop moves on. The done box reads
"Installed 2 of 3 tool(s)" instead of "Done!", and the run ends with
"Failed: cursor — see the [ERR] line under each above" and exit 1. A clean
install is unchanged. Nothing in install_* sets globals the parent reads
afterwards, and the SKIPPED_LOG cleanup trap does not fire in a ( )
subshell, so running tools in a subshell loses nothing.

Parallel mode is untouched; its log replay on failure is item 1 of #917.

test-install-continue-after-failure.sh builds a fixture repo with
gemini-cli and qwen output and no cursor output, installs all three, and
checks that qwen still installs, cursor's error is shown, the summary names
cursor, and the exit is non-zero. It also checks that a clean run still
exits 0. It fails on main at "qwen ... was never installed" and runs in
check-tools.yml.
2026-09-30 04:49:05 -04:00
hotragn 453bc88d47 fix(install): claude-code/copilot and the .md converters collide under --path
path_collision_group put claude-code and copilot in their own group on the
assumption that they write <division>-<slug>.md. They copy the source file
under its own name, and 73 of the 279 agents are named <slug>.md already:
all 20 in game-development/, 38 in specialized/, 6 in spatial-computing/ and
9 elsewhere. For those, the file is the same one gemini-cli, opencode, qwen
and zcode write.

  $ install.sh --tool claude-code,qwen --agent game-designer --path out --no-convert
  [OK]  Claude Code: 1 agents -> out
  [OK]  Qwen Code: installed 1 agents to out
  $ head -2 out/game-designer.md
  ---
  name: 'game-designer'          <- qwen's frontmatter; the Claude Code file is gone

Both tools report success and the install exits 0. A full install of any of
those pairs into one --path loses 73 files the same way.

The six tools are now one group. That also refuses a pair like claude-code
and gemini-cli for a selection with no slug-named agent in it, which could
technically share a directory; the guard works per tool pair, and a full
install collides on 73 files, so treating the pair as colliding is the
honest answer. claude-code with codex, cursor, kimi and the other
distinct-output tools still share a --path as before.

test-install.sh picks a slug-named agent from game-development/ and checks
that claude-code,qwen into one --path is refused and writes nothing. The
refusal comes before conversion, so the case stays cheap.
2026-09-30 04:44:55 -04:00
hotragn 3d80c07945 fix(markdown): seven agents' templates break out of their code blocks
Markdown has no nesting at equal fence length. Inside a ```markdown
template, a ```bash line is text, and the example's bare ``` closes the
template. Seven agents nest example blocks inside a template that way, so on
GitHub the rest of each template renders as headings and prose, and in three
of them the last block then runs open to the end of the file:

  design-ux-architect          code from line 414 to EOF
  ats-validator-architect      code from line 307 to EOF
  master-plan-architect        code from line 102 to EOF
  technical-writer, incident-response-commander, developer-advocate,
  workflow-architect           templates end early, rest renders as markdown

The fix for each is the one CommonMark intends: fence the outer template
with four backticks so the three-backtick examples stay inside it. Nine outer
blocks change; no content does.

Nothing caught this because lib.sh's fence_closes_p accepted "```python" as
a closing fence. CommonMark allows only whitespace after a closing run, and
GitHub renders by that rule. With the old rule the linter and the OpenClaw
split read those seven files one way and GitHub another, so each agreed with
itself and all three disagreed with what users see. fence_closes_p now
requires a bare run, and the OpenClaw split invariant in
test-convert-outputs.sh uses the same rule.

lint-agents.sh gets two errors, each with the file line number:

  - a fence line inside an open block that is long enough to close it but
    carries an info string -- the only way that happens is an attempted
    nest, and the message says how to fix it
  - a block that is still open at the end of the file

The body loop now reads the file from the line after the frontmatter
instead of from $body, which drops every "---" line and so cannot give a
line number. It also keeps a last line with no trailing newline; two agents
end on a closing fence with none, and without that they read as unclosed.

The whole roster lints with 0 errors after the fix; on main the same check
reports 18 errors across the seven files.
scripts/test-lint-fences.sh covers both errors, the suggested fix, tilde
fences, and fence_closes_p directly, and runs in check-tools.yml.
2026-09-30 04:37:58 -04:00
Rudy Celekli 0a22d474b9 Enforce drained recycling and retain cancellation-safe shutdown 2026-09-29 21:05:32 -04:00
Rudy Celekli 2692b54a67 Cast bound tenant UUID to the configuration text type 2026-09-29 21:05:32 -04:00
Rudy Celekli 751378c69b Return no privileged object from unsubscribe 2026-09-29 21:04:36 -04:00
Rudy Celekli f68edd4583 Detect shifts away from constant baselines 2026-09-29 20:58:58 -04:00
Rudy Celekli 43a77bb6e7 Preserve iterable inputs 2026-09-29 20:58:58 -04:00
Rudy Celekli f3eeb37de5 Drain PDF jobs before recycling the browser without recursive locks 2026-09-29 20:55:09 -04:00
Rudy Celekli d3185f2693 Enforce Blender asset validation before exporting selections 2026-09-29 20:55:08 -04:00
Rudy Celekli 3fea7d499f Make the GitHub MCP example runnable and return only issues 2026-09-29 20:55:07 -04:00
Rudy Celekli a0f3137719 Reject empty WeChat HTTP errors without leaving requests pending 2026-09-29 20:55:06 -04:00
Rudy Celekli 73322d5473 Connect the knowledge graph ingestion workflow to its entry node 2026-09-29 20:55:05 -04:00
Rudy Celekli 95ccaa639f Validate YAML reorder pointers before mutating a sequence 2026-09-29 20:55:04 -04:00
Rudy Celekli ae127f185b Keep collaboration replay cursors contiguous and ignore duplicate delivery 2026-09-29 20:55:03 -04:00
Rudy Celekli ca830a9273 Scope tenant RLS context to each database transaction 2026-09-29 20:55:02 -04:00
Rudy Celekli 0e1374a84e Make document chunks match the RAG ingestion contract 2026-09-29 20:55:01 -04:00
Rudy Celekli d7d4c4b44b Fix broken FFmpeg continuation and bind GOP length to frame rate 2026-09-29 20:55:00 -04:00
Rudy Celekli afc1f05209 Keep Electron IPC events out of renderer callbacks 2026-09-29 20:54:59 -04:00
Rudy Celekli 1c5f946d31 Decode compressed audio into valid PCM chunks for transcription 2026-09-29 20:54:58 -04:00
Rudy Celekli 5bc194a0a8 Reject ambiguous email identities before thread reconstruction 2026-09-29 20:54:57 -04:00
Rudy Celekli 4edf283ce4 Include out-of-range observations in model drift PSI 2026-09-29 20:54:56 -04:00
Rudy Celekli 506bd2146b Prevent customer joins from multiplying cloud cost totals 2026-09-29 20:54:55 -04:00
Rudy Celekli fc24e846a6 Report evidence-based QA findings without minimum defect quotas 2026-09-29 20:12:01 -04:00
Rudy Celekli cf43138cb1 Include WCAG 2.1 criteria in the WCAG 2.2 audit baseline 2026-09-29 20:12:01 -04:00
Rudy Celekli d0a2f2fe3d Validate coverage reports and remove undefined coverage-risk adapters 2026-09-29 20:12:01 -04:00
Rudy Celekli 485e1be8e0 Fail performance benchmarks on invalid business responses 2026-09-29 20:12:00 -04:00
Rudy Celekli a36977f644 Capture first-attempt Playwright failure artifacts in automation guidance 2026-09-29 20:12:00 -04:00
Rudy Celekli d52bacb488 Make API Tester Playwright tests runnable and measure real latency 2026-09-29 20:12:00 -04:00
Rudy Celekli b02fa83e4c Point the DevOps CPU alarm at the actual EC2 metric 2026-09-29 20:10:47 -04:00
Rudy Celekli 5c729e483c Make the API Platform OpenAPI example validate 2026-09-29 20:10:47 -04:00
Rudy Celekli 5f51c10ae8 Preserve empty comment arrays in Database Optimizer example 2026-09-29 20:10:47 -04:00
Rudy Celekli 96917920f2 Keep Stripe webhook work recoverable through worker crashes 2026-09-29 20:10:47 -04:00
Rudy Celekli 41ae439960 Bound PostgreSQL migration locks and close the backfill race 2026-09-29 20:10:47 -04:00
Rudy Celekli 011a33280b Fix Data Engineer Gold refresh bounds 2026-09-29 20:10:46 -04:00
Rudy Celekli 06c1a8ddbd fix(i18n): pass message strings to FormatJS runtime 2026-09-29 20:10:00 -04:00
Rudy Celekli a3d1625728 fix(git-workflow): create independent feature worktrees 2026-09-29 20:10:00 -04:00
Rudy Celekli cde130414c Fix PostgreSQL grants in Vault dynamic credential example 2026-09-29 20:08:39 -04:00
Rudy Celekli 82425f3fe3 Correct Chainlink decimal scaling in secure lending example 2026-09-29 20:08:39 -04:00
Rudy Celekli ae70562a83 Create private incident triage directories atomically 2026-09-29 20:08:38 -04:00
Rudy Celekli c23759b7bc Remove attacker-controlled command-line detection exclusions 2026-09-29 20:08:38 -04:00
Rudy Celekli 7b5df086f8 Fix default-deny Kubernetes example with sender egress policies 2026-09-29 20:08:38 -04:00
Rudy Celekli 776cacf430 Fix dependency scanner JSON parsing and incomplete-scan handling 2026-09-29 20:08:38 -04:00
60 changed files with 1438 additions and 423 deletions
+9
View File
@@ -56,3 +56,12 @@ jobs:
- name: Reject unclosed agent frontmatter
run: bash scripts/test-frontmatter-closing.sh
- name: Reject code fences that do not nest or never close
run: bash scripts/test-lint-fences.sh
- name: Unknown options fail; --help lists every option
run: bash scripts/test-cli-usage.sh
- name: Keep installing after one tool fails, then exit non-zero
run: bash scripts/test-install-continue-after-failure.sh
+4
View File
@@ -104,3 +104,7 @@ jobs:
run: |
chmod +x scripts/lint-agents.sh
./scripts/lint-agents.sh
- name: Reject folded frontmatter fixtures
if: steps.rules.outputs.changed == 'true'
run: bash scripts/test-folded-frontmatter.sh
+2
View File
@@ -29,3 +29,5 @@ jobs:
chmod +x scripts/test-install.sh scripts/install.sh
./scripts/test-install.sh
bash scripts/test-install-openclaw-registration.sh
bash scripts/test-install-link-ownership.sh
bash scripts/test-install-parallel-logs.sh
+9
View File
@@ -102,6 +102,15 @@ Browse the agents below and copy/adapt the ones you need!
./scripts/install.sh --tool opencode --division engineering --dry-run
```
`--agent` and `--agents-file` take an agent's slug (as `--list agents` prints it), its display name, or its file name without `.md` — the id the [runbook rosters](strategy/runbooks.json) use — so a runbook's team installs as listed:
```bash
python3 -c 'import json, sys
for r in json.load(open("strategy/runbooks.json"))["runbooks"]:
if r["slug"] == sys.argv[1]: [print(a) for g in r["roster"] for a in g["agents"]]' startup-mvp > team.txt
./scripts/install.sh --tool claude-code --agents-file team.txt
```
> **OpenCode note:** OpenCode's runtime currently registers only ~119 agents and silently drops the rest ([upstream bug](https://github.com/anomalyco/opencode/issues/27988)). Installing a subset with `--division` keeps you under that limit. The installer warns you when a selection would exceed it.
See the [Multi-Tool Integrations](#-multi-tool-integrations) section below for full details.
+2 -2
View File
@@ -326,7 +326,7 @@ grep -i "target\|audience\|goal\|objective" ai/memory-bank/site-setup.md
## 📋 Your Deliverable Template
```markdown
````markdown
# [Project Name] Technical Architecture & UX Foundation
## 🏗️ CSS Architecture
@@ -411,7 +411,7 @@ js/
**Foundation Date**: [Date]
**Developer Handoff**: Ready for LuxuryDeveloper implementation
**Next Steps**: Implement foundation, then add premium polish
```
````
## 💭 Your Communication Style
@@ -40,7 +40,11 @@ You are **API Platform Engineer**, an expert in building APIs that outside devel
### Contract-First OpenAPI (the source of truth, reviewed before code)
```yaml
# The spec is the contract. Consistency here is the whole product.
# A complete minimal document, suitable for schema validation and SDK generation.
openapi: 3.1.0
info:
title: Orders API
version: 1.0.0
paths:
/v1/orders:
post:
@@ -52,10 +56,23 @@ paths:
content: { application/json: { schema: { $ref: '#/components/schemas/OrderCreate' } } }
responses:
'201': { description: Created, content: { application/json: { schema: { $ref: '#/components/schemas/Order' } } } }
'429': { description: Rate limited, headers: { Retry-After: { schema: { type: integer } } } }
'429': { description: Rate limited, headers: { Retry-After: { description: Seconds until retry, schema: { type: integer, minimum: 0 } } } }
default: { description: Error, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
components:
schemas:
OrderCreate:
type: object
required: [product_id, quantity]
properties:
product_id: { type: string, format: uuid }
quantity: { type: integer, minimum: 1 }
Order:
type: object
required: [id, product_id, quantity]
properties:
id: { type: string, format: uuid }
product_id: { type: string, format: uuid }
quantity: { type: integer, minimum: 1 }
Error: # ONE error shape, used everywhere — no exceptions
type: object
required: [code, message]
@@ -63,9 +80,11 @@ components:
code: { type: string, example: rate_limit_exceeded } # stable, machine-readable
message: { type: string, example: "API rate limit exceeded; retry after 30s" }
details: { type: object, description: "Field-level or contextual detail for self-diagnosis" }
request_id:{ type: string, description: "Echo this to support — traceable on our side" }
request_id: { type: string, description: "Echo this to support — traceable on our side" }
```
Validate the whole document with an OpenAPI 3.1 validator before generating clients: YAML parsing alone cannot catch missing required document metadata or unresolved `$ref` targets. The minimal example defines every referenced schema; keep that invariant when extracting a larger contract. See the [OpenAPI 3.1 specification](https://spec.openapis.org/oas/v3.1.1.html).
### Backward-Compatibility Rules (memorize the two columns)
| Safe (additive — no version bump) | Breaking (needs new version + deprecation) |
@@ -264,7 +264,7 @@ When performing an ATS audit or designing an ATS validation engine, you must pro
### Deliverable 5: Agent-Native Export Prompt
```markdown
````markdown
## 🤖 Prompt Pronto para Agentes Externos (Claude / ChatGPT / Cursor)
```markdown
@@ -286,7 +286,7 @@ REGRAS RÍGIDAS:
3. Não exceda 30 palavras por bullet (evite sobrecarga cognitiva).
4. Retorne apenas os bullets reescritos formatados em Markdown.
```
```
````
## 🔄 Your Workflow Process
+16 -3
View File
@@ -61,6 +61,7 @@ You are a **Data Engineer**, an expert in designing, building, and operating the
### Spark Pipeline (PySpark + Delta Lake)
```python
from datetime import date
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, current_timestamp, sha2, concat_ws, lit
from delta.tables import DeltaTable
@@ -99,8 +100,18 @@ def upsert_silver(bronze_table: str, silver_table: str, pk_cols: list[str]) -> N
source.write.format("delta").mode("overwrite").save(silver_table)
# ── Gold: aggregated business metric ─────────────────────────────────────────
def build_gold_daily_revenue(silver_orders: str, gold_table: str) -> None:
df = spark.read.format("delta").load(silver_orders)
def build_gold_daily_revenue(
silver_orders: str, gold_table: str, start_date: date, end_date: date
) -> None:
# Recompute an explicit half-open DATE window, including dates with no sales.
# Deriving bounds from completed rows would leave stale revenue on an empty day.
if start_date >= end_date:
raise ValueError("start_date must be earlier than end_date")
predicate = (
f"order_date >= '{start_date.isoformat()}' "
f"AND order_date < '{end_date.isoformat()}'"
)
df = spark.read.format("delta").load(silver_orders).filter(predicate)
gold = df.filter(col("status") == "completed") \
.groupBy("order_date", "region", "product_category") \
.agg({"revenue": "sum", "order_id": "count"}) \
@@ -108,10 +119,12 @@ def build_gold_daily_revenue(silver_orders: str, gold_table: str) -> None:
.withColumnRenamed("count(order_id)", "order_count") \
.withColumn("_refreshed_at", current_timestamp())
gold.write.format("delta").mode("overwrite") \
.option("replaceWhere", f"order_date >= '{gold['order_date'].min()}'") \
.option("replaceWhere", predicate) \
.save(gold_table)
```
Run the Gold refresh against a complete Silver snapshot for the requested date window, not a partial event batch. Delta `replaceWhere` replaces exactly that window even when the aggregate is empty; dates outside it must remain untouched. Keep its default predicate constraint check enabled. For example, refreshing `[2026-09-01, 2026-09-02)` after an order is refunded must remove the old September 1 revenue, while preserving September 2 and later results. See [Delta selective overwrite](https://docs.delta.io/delta-batch/#selective-overwrite).
### dbt Data Quality Contract
```yaml
# models/silver/schema.yml
+12 -5
View File
@@ -73,16 +73,23 @@ SELECT * FROM comments WHERE post_id = ?;
EXPLAIN ANALYZE
SELECT
p.id, p.title, p.content,
json_agg(json_build_object(
'id', c.id,
'content', c.content,
'author', c.author
)) as comments
COALESCE(
json_agg(json_build_object(
'id', c.id,
'content', c.content,
'author', c.author
) ORDER BY c.id) FILTER (WHERE c.id IS NOT NULL),
'[]'::json
) AS comments
FROM posts p
LEFT JOIN comments c ON c.post_id = p.id
WHERE p.user_id = 123
GROUP BY p.id;
-- Regression cases: no comments => []; two comments => two ordered objects.
-- FILTER removes the synthetic NULL row from LEFT JOIN; COALESCE turns an
-- empty aggregate into the same array shape as a populated one.
-- Check the query plan:
-- Look for: Seq Scan (bad), Index Scan (good), Bitmap Heap Scan (okay)
-- Check: actual time vs planned time, rows vs estimated rows
@@ -19,17 +19,17 @@ You are **Database Reliability Engineer** (DBRE), an expert in keeping databases
## 🎯 Your Core Mission
- Design high availability: replication topology, automated failover, and quorum so a single node loss is a non-event, not an outage
- Guarantee recoverability: automated backups, point-in-time recovery, and — the part everyone skips — regularly *tested* restores against real RPO/RTO targets
- Make schema change safe: zero-downtime online migrations that never take a lock that stalls production, with an expand-contract discipline and a rollback plan
- Make schema change safe: expand-contract migrations with measured lock budgets, bounded waits, batched backfills, and a rollback plan compatible with deployed writers
- Protect the database from the application: connection pooling, sane limits, and backpressure so a client bug can't exhaust connections and topple the datastore
- Rehearse disaster: scheduled failover and restore drills, documented runbooks, and DR that's been executed, not just diagrammed
- **Default requirement**: Every backup strategy is validated by a real restore; every failover path is drilled; every schema migration is proven non-blocking before it touches production
- **Default requirement**: Every backup strategy is validated by a real restore; every failover path is drilled; every schema migration has tested lock and statement budgets before it touches production
## 🚨 Critical Rules You Must Follow
1. **An untested backup is not a backup.** Backups that have never been restored are a hope, not a recovery plan. Automate restore verification on a schedule and measure the actual RTO — the first time you test a restore must never be during an incident.
2. **Know your RPO and RTO, and prove you meet them.** How much data can you lose (RPO) and how long can you be down (RTO)? These are business decisions with technical consequences. Design backup frequency, replication, and failover to hit them, then verify with drills.
3. **Failover must be drilled until it's boring.** An automated failover that's never been exercised will fail when it matters — promoting a lagging replica, splitting brain, or losing writes. Rehearse it on a schedule and fix what the drill exposes.
4. **Never run a schema migration that takes a blocking lock in production.** A naive `ALTER`/`ADD COLUMN`/index build can lock a hot table and stall every query behind it. Use online/concurrent operations, expand-contract sequencing, and batched backfills — and verify the lock behavior before running it.
4. **Budget every schema migration's locks.** Even metadata-only PostgreSQL `ADD COLUMN` takes an `ACCESS EXCLUSIVE` lock. Use a short `lock_timeout`, bounded statements, separate transactions, and a retry plan so waiting DDL cannot queue traffic indefinitely. Verify the engine's actual lock modes and keep scans/backfills out of exclusive-lock transactions.
5. **Guard the connection layer.** Databases have hard connection limits; applications open connections faster than DBs can serve them. A pooler (PgBouncer / ProxySQL / equivalent) plus sane per-service limits is mandatory — connection exhaustion takes down a healthy database from the outside.
6. **Replication lag is a correctness issue, not just a metric.** Reading from a lagging replica serves stale data; failing over to one loses writes. Monitor lag, gate read-after-write on it, and never promote a replica that's behind without understanding the data loss.
7. **Every destructive or heavy operation needs a rollback and a blast-radius estimate.** Migrations, failovers, and large deletes get a written back-out plan and an impact assessment before execution — on a stateful system there is no `git revert`.
@@ -77,27 +77,66 @@ Drill this on a schedule. A failover you haven't run is a failover you don't hav
### Zero-Downtime Migration: Expand-Contract
```sql
-- WRONG: locks the hot table, stalls production behind it
-- ALTER TABLE orders ADD COLUMN status VARCHAR NOT NULL DEFAULT 'pending'; (blocking on many DBs)
-- PostgreSQL example: short exclusive locks are still locks, not "non-blocking" DDL.
-- On timeout, roll back the entire failed transaction and retry off peak.
-- 1. EXPAND in its own short transaction; do not backfill while holding this lock.
BEGIN;
SET LOCAL lock_timeout = '1s';
SET LOCAL statement_timeout = '5s';
ALTER TABLE orders ADD COLUMN status VARCHAR;
COMMIT;
-- RIGHT: expand-contract, no blocking lock, reversible at every step
-- 1. EXPAND — add nullable column (fast, metadata-only), no default backfill lock
ALTER TABLE orders ADD COLUMN status VARCHAR; -- instant, non-blocking
-- 2. Set the default separately: new inserts that omit status receive 'pending'.
-- Existing rows remain NULL, so unrelated UPDATEs can continue before backfill.
BEGIN;
SET LOCAL lock_timeout = '1s';
SET LOCAL statement_timeout = '5s';
ALTER TABLE orders ALTER COLUMN status SET DEFAULT 'pending';
COMMIT;
-- 2. BACKFILL in batches so no single statement holds a long lock or bloats WAL
UPDATE orders SET status = 'pending' WHERE status IS NULL AND id BETWEEN :lo AND :hi; -- loop
-- 3. Deploy writers that never explicitly insert or update status to NULL;
-- wait for ALL old writers to drain. Keep reads compatible with historical NULLs.
-- 4. BACKFILL bounded batches, committing each batch (:lo/:hi are runner parameters).
UPDATE orders SET status = 'pending'
WHERE status IS NULL AND id BETWEEN :lo AND :hi;
-- 3. Dual-write from the app (new code writes status), deploy, let it bake
-- 4. Add the constraint only after backfill is complete, validated separately:
ALTER TABLE orders ADD CONSTRAINT status_not_null CHECK (status IS NOT NULL) NOT VALID;
ALTER TABLE orders VALIDATE CONSTRAINT status_not_null; -- validates without a full-table lock
-- 5. CONTRACT — remove old column/paths in a later release, once nothing reads them
-- Every step is independently deployable and reversible. No maintenance window.
-- 5. Gate new NULLs AFTER backfill: even a NOT VALID CHECK checks every UPDATE,
-- including an unrelated column update on a legacy row whose status is NULL.
BEGIN;
SET LOCAL lock_timeout = '1s';
SET LOCAL statement_timeout = '5s';
ALTER TABLE orders ADD CONSTRAINT status_not_null
CHECK (status IS NOT NULL) NOT VALID;
COMMIT;
-- Indexes: always concurrently, so reads/writes continue during the build
-- 6. VALIDATE separately: SHARE UPDATE EXCLUSIVE permits normal reads/writes,
-- but can conflict with other maintenance/DDL. Set a realistic scan budget.
BEGIN;
SET LOCAL lock_timeout = '1s';
SET LOCAL statement_timeout = '10min';
ALTER TABLE orders VALIDATE CONSTRAINT status_not_null;
COMMIT;
-- 7. Optional SET NOT NULL: on PostgreSQL 12+, a valid CHECK skips the table scan,
-- but an ACCESS EXCLUSIVE lock is still needed. Drop the CHECK in a later step.
BEGIN;
SET LOCAL lock_timeout = '1s';
SET LOCAL statement_timeout = '5s';
ALTER TABLE orders ALTER COLUMN status SET NOT NULL;
COMMIT;
-- CONTRACT old read paths only in a later release. After step 5, rolling back
-- to a writer that explicitly writes NULL is unsafe until the constraint is relaxed.
-- Index build is outside a transaction; concurrent builds still take locks.
-- A failed concurrent build can leave an INVALID index: inspect it, then drop
-- that invalid index before retrying (outside a transaction as well).
CREATE INDEX CONCURRENTLY idx_orders_status ON orders (status);
```
For a constant default such as this example's `'pending'`, PostgreSQL 11+ can instead add `status VARCHAR NOT NULL DEFAULT 'pending'` in one metadata-only operation, still under a short exclusive lock. The staged backfill pattern is needed when historical values must be computed per row; adapt the batch expression to that computation.
See [PostgreSQL ALTER TABLE lock and constraint semantics](https://www.postgresql.org/docs/current/sql-altertable.html). Test an open reader that forces step 1 to time out, an unrelated UPDATE on a legacy NULL row before its backfill, and an explicit NULL write after step 5. A failed batch can be replayed because it updates only NULL rows; validation is the proof that all historical rows now satisfy the invariant.
### Reliability Metrics & Guards
| Signal | Why it matters | Guard / alert |
@@ -73,7 +73,12 @@ ipcMain.handle('project:export', async (event, raw) => {
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('app', {
exportProject: (req: unknown) => ipcRenderer.invoke('project:export', req),
onUpdateReady: (cb: () => void) => ipcRenderer.on('update:ready', cb),
onUpdateReady: (cb: () => void) => {
// Electron's event object stays in preload; renderer callbacks receive no IPC internals.
const listener = () => cb();
ipcRenderer.on('update:ready', listener);
return () => { ipcRenderer.removeListener('update:ready', listener); };
},
});
```
+11 -3
View File
@@ -171,15 +171,23 @@ resource "aws_cloudwatch_metric_alarm" "high_cpu" {
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/ApplicationELB"
period = "120"
namespace = "AWS/EC2"
dimensions = {
AutoScalingGroupName = aws_autoscaling_group.app.name
}
# Matches EC2 basic monitoring's five-minute publication interval.
period = "300"
statistic = "Average"
threshold = "80"
# Missing telemetry is unknown, not evidence that CPU is healthy.
treat_missing_data = "missing"
alarm_actions = [aws_sns_topic.alerts.arn]
}
```
Before enabling an alarm, confirm its namespace, metric name, dimensions, and cadence against actual published datapoints. `CPUUtilization` belongs to [AWS/EC2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html), and `AutoScalingGroupName` selects this application's instances. `AWS/ApplicationELB` publishes load-balancer metrics, not instance CPU. Basic EC2 monitoring publishes every five minutes; use detailed monitoring explicitly if you need one-minute detection. Treat missing data as unknown and monitor telemetry loss separately.
### Monitoring and Alerting Configuration
```yaml
# Prometheus Configuration
@@ -128,7 +128,16 @@ def reconstruct_thread(messages):
- Quoted replies duplicate content (20-msg thread = ~4-5x token bloat)
- Thread forks when people reply to different messages in the chain
"""
# Build reply graph from In-Reply-To and References headers
# Reject ambiguous identities before building or mutating the graph.
# Missing/duplicate Message-ID must go to a quarantine/resolution path;
# silently using None (or a reused ID) overwrites an unrelated message.
messages = list(messages) # preserve support for one-pass message iterables
message_ids = [msg.get("message_id") for msg in messages]
if any(not isinstance(mid, str) or not mid.strip() for mid in message_ids):
raise ValueError("Every message needs a nonempty Message-ID")
if len(set(message_ids)) != len(message_ids):
raise ValueError("Duplicate Message-ID: resolve identity before reconstruction")
graph = {}
for msg in messages:
parent_id = msg["in_reply_to"]
+23 -12
View File
@@ -84,18 +84,29 @@ A commitment you don't fully utilize is a discount you paid for and threw away.
### Unit Economics Dashboard (spend judged against value)
```sql
-- Cost per active customer, trended — the number that tells growth from waste.
-- Total cloud cost rising is fine IF cost-per-unit is flat or falling.
SELECT
date_trunc('month', usage_date) AS month,
SUM(unblended_cost) AS total_cloud_cost,
COUNT(DISTINCT customer_id) AS active_customers,
SUM(unblended_cost) / NULLIF(COUNT(DISTINCT customer_id), 0) AS cost_per_customer,
SUM(unblended_cost) FILTER (WHERE tag_environment = 'prod') AS prod_cost,
SUM(unblended_cost) FILTER (WHERE tag_environment != 'prod') AS nonprod_cost
FROM cost_and_usage
JOIN customer_activity USING (usage_date)
GROUP BY 1 ORDER BY 1;
-- Aggregate each source to the reporting grain BEFORE joining.
-- cost_and_usage: multiple line items/day; customer_activity: multiple events/day.
WITH monthly_cost AS (
SELECT date_trunc('month', usage_date) AS month,
SUM(unblended_cost) AS total_cloud_cost,
SUM(unblended_cost) FILTER (WHERE tag_environment = 'prod') AS prod_cost,
SUM(unblended_cost) FILTER (WHERE tag_environment != 'prod') AS nonprod_cost
FROM cost_and_usage
GROUP BY 1
), monthly_customers AS (
SELECT date_trunc('month', usage_date) AS month,
COUNT(DISTINCT customer_id) AS active_customers
FROM customer_activity
GROUP BY 1
)
SELECT c.month, c.total_cloud_cost,
COALESCE(a.active_customers, 0) AS active_customers,
c.total_cloud_cost / NULLIF(a.active_customers, 0) AS cost_per_customer,
c.prod_cost, c.nonprod_cost
FROM monthly_cost c
LEFT JOIN monthly_customers a USING (month)
ORDER BY c.month;
-- Keep cost-only days/months; no observed active customers means unknown unit cost.
-- Present alongside: allocated %, commitment coverage %, commitment utilization %.
```
+18 -4
View File
@@ -56,16 +56,30 @@ develop ───●───●───●───●───●────
### Starting Work
```bash
git fetch origin
git checkout -b feat/my-feature origin/main
# Or with worktrees for parallel work:
git worktree add ../my-feature feat/my-feature
git switch --no-track -c feat/my-feature origin/main
# Publish your feature branch and set its upstream explicitly:
git push -u origin feat/my-feature
```
For parallel work, use this **instead of** creating the branch in the current
checkout. Git cannot check out the same branch in two worktrees:
```bash
git fetch origin
git worktree add --no-track -b feat/my-feature ../my-feature origin/main
cd ../my-feature
git push -u origin feat/my-feature
```
`--no-track` prevents a new feature branch from inheriting `origin/main` as its
upstream. After the first push, its upstream is `origin/feat/my-feature`.
### Clean Up Before PR
```bash
git fetch origin
git rebase -i origin/main # squash fixups, reword messages
git push --force-with-lease # safe force push to your branch
# Only rewrite your own feature branch, with collaborators' agreement:
git push --force-with-lease origin HEAD:feat/my-feature
```
### Finishing a Branch
+19 -4
View File
@@ -54,16 +54,31 @@ You are **Internationalization Engineer**, an expert in making software genuinel
```
```javascript
// Rendering with FormatJS — the same message file drives web, and its format
// (ICU) is what Android, iOS, and most TMS platforms speak natively.
import { createIntl } from '@formatjs/intl';
// Rendering with FormatJS: retain descriptions in the translator catalog,
// but pass message strings (or compiled ASTs) to createIntl, not descriptors.
import { createIntl, createIntlCache } from '@formatjs/intl';
const intl = createIntl({ locale: 'ar', messages: arMessages });
const arMessages = {
'cart.itemCount': {
message: '{count, plural, =0 {سلتك فارغة} one {عنصر واحد} two {عنصران} few {# عناصر} many {# عنصرًا} other {# عنصر}}',
description: 'Cart header; count = item count.',
},
};
const messages = Object.fromEntries(
Object.entries(arMessages).map(([id, descriptor]) => [id, descriptor.message])
);
const intl = createIntl({ locale: 'ar', messages }, createIntlCache());
intl.formatMessage({ id: 'cart.itemCount' }, { count: 3 });
// Arabic resolves count=3 to the CLDR "few" category — a form English doesn't have,
// which is exactly why the ternary-operator version was a bug.
```
Keep each target locale's ICU text and translator metadata separate at the
runtime boundary. Passing `{message, description}` objects as `messages` entries
causes formatting errors and can display the message ID instead of the translation.
Mobile resource formats need a platform-specific export; an ICU catalog is not
automatically an Android resource or an iOS String Catalog.
### Locale-Aware Formatting: Delete the Hand-Rolled Helpers
```javascript
@@ -132,14 +132,27 @@ challengeStore.put(user.id, options.challenge, { ttlSeconds: 300 });
### Multi-Tenant Authorization: Isolation Below the Application
```sql
-- Postgres row-level security: tenant scoping the ORM can't forget
-- Use a restricted application role (no superuser or BYPASSRLS).
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;
ALTER TABLE documents FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON documents
USING (tenant_id = current_setting('app.tenant_id')::uuid);
USING (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::uuid)
WITH CHECK (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::uuid);
-- Set from the AUTHENTICATED session at connection checkout — never from request input:
-- SET app.tenant_id = '<tenant uuid from the verified session>';
-- EVERY request runs all its queries on this same connection and transaction.
-- Bind the authenticated tenant UUID using the driver's parameter API.
BEGIN;
SELECT set_config('app.tenant_id', CAST(:authenticated_tenant_id AS text), true);
-- SELECT/INSERT/UPDATE/DELETE documents here, then COMMIT (or ROLLBACK on error).
COMMIT;
-- The true flag makes context transaction-local: pool reuse cannot carry a
-- previous tenant into the next request. Missing context denies access.
-- FORCE also subjects the table owner to RLS; privileged maintenance roles
-- still bypass it and must never be used by request-serving connections.
-- Objects called by requests must also use the caller's restricted privileges:
-- avoid privileged SECURITY DEFINER functions and bypass-capable view owners;
-- use security_invoker views where supported.
```
## 🔄 Your Workflow Process
@@ -81,7 +81,7 @@ You are **Incident Response Commander**, an expert incident management specialis
```
### Incident Response Runbook Template
```markdown
````markdown
# Runbook: [Service/Failure Scenario Name]
## Quick Reference
@@ -146,7 +146,7 @@ kubectl autoscale deployment/<service> -n production \
- Internal: Post update in #incidents Slack channel
- External: Update [status page link] if customer-facing
- Follow-up: Create post-mortem document within 24 hours
```
````
### Post-Mortem Document Template
```markdown
@@ -267,6 +267,7 @@ def build_ingest_graph(driver):
g.add_node("merge", merge_node) # MERGE into Neo4j
g.add_node("detect", detect_node) # contradiction Cypher
g.add_node("verify", verify_node) # integrity gates
g.set_entry_point("extract")
g.add_edge("extract", "merge")
g.add_edge("merge", "detect")
g.add_edge("detect", "verify")
@@ -29,7 +29,7 @@ You are **Payments & Billing Engineer**, an expert in building payment integrati
1. **Never touch raw card data.** Card numbers go from the customer's browser to the processor via hosted fields or SDK tokenization. If a PAN can reach your server, the design is wrong — that is the difference between SAQ A and a full PCI DSS audit.
2. **Every mutation carries an idempotency key.** Charges, refunds, and subscription changes must be safely retryable. Derive the key from the business operation (order ID + attempt), not from a random UUID per HTTP call.
3. **Webhooks are the source of truth, not the redirect.** Fulfill on `payment_intent.succeeded` (or the PSP equivalent), never on the customer returning to your success page. Customers close tabs; webhooks don't.
4. **Verify signatures and deduplicate by event ID.** Reject unsigned or stale webhook payloads, persist processed event IDs, and make handlers safe to run twice.
4. **Verify signatures and persist recoverable work.** Reject unsigned or stale webhook payloads; durably store verified events before acknowledgment. Deduplicate acceptance by event ID, mark completion only after successful processing, and make side effects safe to replay after a worker crash.
5. **Store money as integers in minor units.** Amounts are `4999` cents with an ISO 4217 currency code — never floats, and never a bare number without its currency. Beware zero-decimal currencies like JPY.
6. **Model every state, especially the unhappy ones.** `requires_action` (3DS), `processing`, partial refunds, disputes, and failed dunning retries are normal operating states, not edge cases to log-and-ignore.
7. **Reconcile before you celebrate.** A green test suite proves the code path; only a payout-to-ledger reconciliation proves the money. Automate it daily and alert on any drift.
@@ -60,42 +60,76 @@ export async function createPaymentForOrder(order: Order): Promise<Stripe.Paymen
}
```
### Webhook Handler: Signature, Dedupe, Out-of-Order Safety
### Webhook Handler: Durable Acceptance Before Acknowledgment
Persist verified events in a durable inbox before returning `2xx`. An event ID alone is not a "processed" marker: if fulfillment crashes after inserting it, a retry must still find pending work. This example uses an application-owned inbox adapter with these explicit guarantees:
```typescript
export async function handleStripeWebhook(req: Request): Promise<Response> {
// 1. Verify the signature against the raw body — parsed JSON breaks verification
const event = stripe.webhooks.constructEvent(
await req.text(),
req.headers.get('stripe-signature')!,
process.env.STRIPE_WEBHOOK_SECRET!
);
interface WebhookInbox {
// Atomic insert of ID + complete payload as pending, with a UNIQUE(event_id).
// A duplicate never overwrites payload or resets completed work. Resolve only
// after durable commit; reject on storage failure so the processor retries.
accept(event: Stripe.Event): Promise<void>;
// Atomically lease pending/expired work (e.g. FOR UPDATE SKIP LOCKED), increment
// attempts, and return its payload. Reclaim expired leases after crashes;
// move exhausted jobs to an inspectable dead-letter state instead of retrying forever.
claim(maxAttempts: number): Promise<Stripe.Event | null>;
// Mark complete only after side effects succeed. Pending/in-progress jobs
// must remain retryable; the worker runner leases jobs and reclaims crashes.
complete(eventId: string): Promise<void>;
}
// 2. Deduplicate: at-least-once delivery means "twice" in practice
const alreadyProcessed = await db.webhookEvents.insertIgnore({ id: event.id });
if (alreadyProcessed) return new Response('duplicate', { status: 200 });
export async function handleStripeWebhook(
req: Request, inbox: WebhookInbox
): Promise<Response> {
const signature = req.headers.get('stripe-signature');
if (!signature) return new Response('missing signature', { status: 400 });
// 3. Never trust event order — re-fetch current state instead of applying deltas
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
await req.text(), signature, process.env.STRIPE_WEBHOOK_SECRET!
);
} catch {
return new Response('invalid signature', { status: 400 });
}
try {
await inbox.accept(event); // includes duplicates whose original work is pending
return new Response('accepted', { status: 200 });
} catch {
return new Response('storage unavailable', { status: 503 });
}
}
// The durable inbox worker invokes this with a leased pending event.
// If it throws, retry with backoff; never mark the event complete in a finally block.
export async function processStripeEvent(
event: Stripe.Event, inbox: WebhookInbox
): Promise<void> {
switch (event.type) {
case 'payment_intent.succeeded': {
// Events can arrive out of order: re-fetch current processor state before acting.
const pi = await stripe.paymentIntents.retrieve(
(event.data.object as Stripe.PaymentIntent).id
);
if (pi.status === 'succeeded') {
await fulfillOrder(pi.metadata.order_id); // must itself be idempotent
await fulfillOrder(pi.metadata.order_id); // unique order fulfillment/outbox
}
break;
}
case 'charge.dispute.created':
await freezeOrderAndNotifyFinance(event); // evidence deadline starts NOW
await freezeOrderAndNotifyFinance(event); // dedupe notification by event.id
break;
}
// 4. Return 2xx fast; do heavy work in a queue so the PSP doesn't retry-storm you
return new Response('ok', { status: 200 });
await inbox.complete(event.id);
}
```
The worker scheduler, durable adapter, and domain handlers are application dependencies. `fulfillOrder` must atomically record fulfillment and any delivery outbox, or use downstream idempotency keys: a crash after the side effect but before `complete` replays the event. Distinct processor event IDs for the same order must also converge to one fulfillment. Signature failures return `400`; storage failures return `503`; acknowledged events remain recoverable without relying on processor redelivery.
Test four boundaries: failure before inbox commit, duplicate delivery while pending, worker failure before fulfillment, and worker crash after fulfillment but before completion. In each case the pending event must eventually complete with exactly one fulfillment. See [Stripe webhook delivery and signature guidance](https://docs.stripe.com/webhooks).
### Subscription Lifecycle State Machine
```text
+100 -66
View File
@@ -333,104 +333,138 @@ export class PageGeometryEngine {
Maintains a warm Chromium browser instance with pooled, isolated `BrowserContext` objects, concurrency rate limiting, route blocking for external noise, and scheduled recycling to deliver sub-80ms compilations:
```python
# cv_pdf_pool.py: High-Throughput Browser Context Pool
# cv_pdf_pool.py: Drain active jobs before recycling the shared browser.
import asyncio
import logging
from typing import Optional
from playwright.async_api import async_playwright, Browser, BrowserContext, Playwright
from playwright.async_api import async_playwright, Browser, Playwright
logger = logging.getLogger("pdf_pool")
class PlaywrightPDFPool:
def __init__(self, max_concurrency: int = 4, max_jobs_before_recycle: int = 500):
self.max_concurrency = max_concurrency
if max_concurrency < 1 or max_jobs_before_recycle < 1:
raise ValueError("Pool limits must be positive")
self.max_jobs_before_recycle = max_jobs_before_recycle
self.semaphore = asyncio.Semaphore(max_concurrency)
self.job_counter = 0
self.playwright: Optional[Playwright] = None
self.browser: Optional[Browser] = None
self._lock = asyncio.Lock()
self._condition = asyncio.Condition()
self._active_jobs = 0
self._recycling = False
self._closed = False
self._shutdown_task = None
async def initialize(self):
async with self._lock:
if self.browser and self.browser.is_connected():
return
self.playwright = await async_playwright().start()
async def _close_locked(self):
browser, playwright = self.browser, self.playwright
self.browser = self.playwright = None
try:
if browser:
await browser.close()
finally:
if playwright:
await playwright.stop()
async def _initialize_locked(self):
if self.browser and self.browser.is_connected():
return
if self._active_jobs:
raise RuntimeError("Disconnected browser still has active jobs")
await self._close_locked()
self.playwright = await async_playwright().start()
try:
self.browser = await self.playwright.chromium.launch(
headless=True,
args=[
"--disable-background-networking",
"--disable-gpu",
"--disable-dev-shm-usage",
"--no-sandbox",
"--font-render-hinting=none"
]
args=["--disable-background-networking", "--disable-gpu",
"--disable-dev-shm-usage", "--no-sandbox", "--font-render-hinting=none"]
)
self.job_counter = 0
logger.info("Playwright PDF Pool initialized with warm Chromium instance.")
except BaseException:
await self._close_locked()
raise
self.job_counter = 0
async def render_pdf(
self,
html_content: str,
width_mm: float = 210.0,
height_mm: float = 297.0
) -> bytes:
await self.initialize()
async def initialize(self):
async with self._condition:
await self._condition.wait_for(lambda: not self._recycling or self._closed)
if self._closed:
raise RuntimeError("PDF pool is shut down")
await self._initialize_locked()
async def render_pdf(self, html_content: str, width_mm: float = 210.0,
height_mm: float = 297.0) -> bytes:
async with self.semaphore:
self.job_counter += 1
if self.job_counter >= self.max_jobs_before_recycle:
logger.info("Recycling browser process after %d jobs.", self.job_counter)
await self.recycle()
# Create isolated context for the request
context: BrowserContext = await self.browser.new_context(
viewport={"width": int(width_mm * 96 / 25.4), "height": int(height_mm * 96 / 25.4)},
device_scale_factor=1.0
)
async with self._condition:
# At the threshold, stop admitting new jobs until this
# generation drains; sustained traffic cannot starve recycling.
await self._condition.wait_for(lambda: self._closed or (
not self._recycling and (self.job_counter < self.max_jobs_before_recycle
or self._active_jobs == 0)
))
if self._closed:
raise RuntimeError("PDF pool is shut down")
# Drain at the configured threshold without interrupting active
# renders or reacquiring a lock held by the same coroutine.
if self._active_jobs == 0 and self.job_counter >= self.max_jobs_before_recycle:
await self._close_locked()
await self._initialize_locked()
browser = self.browser
self._active_jobs += 1
self.job_counter += 1
context = None
try:
page = await context.new_page()
# Abort tracking and off-target external requests
await page.route(
"**/*",
lambda route: route.abort() if route.request.resource_type in ["media", "websocket"] else route.continue_()
context = await browser.new_context(
viewport={"width": int(width_mm * 96 / 25.4), "height": int(height_mm * 96 / 25.4)},
device_scale_factor=1.0
)
# Load HTML with networkidle guarantee
page = await context.new_page()
await page.route("**/*", lambda route: route.abort()
if route.request.resource_type in ["media", "websocket"]
else route.continue_())
await page.set_content(html_content, wait_until="networkidle")
await page.evaluate("document.fonts.ready")
# Generate tagged, vector-clean PDF via CDP
pdf_bytes = await page.pdf(
width=f"{width_mm}mm",
height=f"{height_mm}mm",
print_background=True,
prefer_css_page_size=True,
tagged=True,
return await page.pdf(
width=f"{width_mm}mm", height=f"{height_mm}mm",
print_background=True, prefer_css_page_size=True, tagged=True,
margin={"top": "0mm", "right": "0mm", "bottom": "0mm", "left": "0mm"}
)
return pdf_bytes
finally:
await context.close()
try:
if context:
await context.close()
finally:
async with self._condition:
self._active_jobs -= 1
self._condition.notify_all()
async def recycle(self):
async with self._lock:
if self.browser:
await self.browser.close()
if self.playwright:
await self.playwright.stop()
self.browser = None
self.playwright = None
await self.initialize()
async with self._condition:
await self._condition.wait_for(lambda: not self._recycling or self._closed)
if self._closed:
raise RuntimeError("PDF pool is shut down")
self._recycling = True
try:
await self._condition.wait_for(lambda: self._active_jobs == 0)
await self._close_locked()
await self._initialize_locked()
finally:
self._recycling = False
self._condition.notify_all()
async def _finish_shutdown(self):
async with self._condition:
self._condition.notify_all()
await self._condition.wait_for(lambda: self._active_jobs == 0 and not self._recycling)
await self._close_locked()
async def shutdown(self):
async with self._lock:
if self.browser:
await self.browser.close()
if self.playwright:
await self.playwright.stop()
# Retain cleanup: cancelling a caller must not abandon the browser
# after in-flight renders finish.
self._closed = True
if self._shutdown_task is None:
self._shutdown_task = asyncio.create_task(self._finish_shutdown())
await asyncio.shield(self._shutdown_task)
```
### 4. 1:1 Sheet Canvas Viewport Scaler Architecture (CSS & React)
@@ -70,7 +70,7 @@ You've built these systems for real workloads: multilingual corpora, domain-spec
### Chunking Strategy — Semantic + Structural
```python
from langchain.text_splitter import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter
from langchain_text_splitters import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter
def chunk_document(text: str, doc_type: str) -> list[dict]:
"""
@@ -96,7 +96,7 @@ def chunk_document(text: str, doc_type: str) -> list[dict]:
for doc in header_chunks:
sub_chunks = char_splitter.split_documents([doc])
chunks.extend(sub_chunks)
return chunks
return [{"content": doc.page_content, "metadata": doc.metadata} for doc in chunks]
else:
# Semantic chunking for unstructured text
@@ -105,7 +105,10 @@ def chunk_document(text: str, doc_type: str) -> list[dict]:
chunk_overlap=80,
separators=["\n\n", "\n", ". ", "! ", "? ", " "]
)
return splitter.create_documents([text])
return [
{"content": doc.page_content, "metadata": doc.metadata}
for doc in splitter.create_documents([text])
]
```
### pgvector Schema & HNSW Index
@@ -141,6 +144,7 @@ CREATE INDEX ON document_chunks (document_id);
```python
import asyncio
import json
from openai import AsyncOpenAI
from pgvector.asyncpg import register_vector
import asyncpg
@@ -177,7 +181,7 @@ async def ingest_document(document_id: str, chunks: list[dict], pool: asyncpg.Po
VALUES ($1, $2, $3, $4, $5)
""",
[
(document_id, c["content"], emb, idx, c.get("metadata", {}))
(document_id, c["content"], emb, idx, json.dumps(c.get("metadata", {})))
for idx, (c, emb) in enumerate(zip(chunks, embeddings))
]
)
@@ -41,7 +41,8 @@ You are **Realtime Collaboration Engineer**, an expert in the systems behind liv
```typescript
// The contract: server assigns seq to every op; client acks what it has applied;
// resume replays the gap. Duplicates are impossible by construction (opId dedupe).
// resume replays the gap. Server opId dedupe prevents duplicate log entries;
// clients must separately ignore replayed deliveries and reject sequence gaps.
class SyncConnection {
private lastServerSeq = 0; // highest seq applied locally
private pending = new Map<string, Op>(); // sent, not yet acked
@@ -66,9 +67,16 @@ class SyncConnection {
private receive(msg: ServerMsg) {
if (msg.type === 'op') {
this.lastServerSeq = msg.seq; // server ordering is truth
this.pending.delete(msg.opId); // ack of our own op, or...
this.applyRemote(msg); // ...someone else's, transformed
if (msg.seq <= this.lastServerSeq) return; // replay: already applied
if (msg.seq !== this.lastServerSeq + 1) {
// Keep the contiguous cursor: reconnect/replay from the last applied op.
// Closing triggers the existing onclose reconnect path.
this.ws.close();
return;
}
this.applyRemote(msg); // may throw; do not advance yet
this.lastServerSeq = msg.seq;
this.pending.delete(msg.opId); // ack only after successful apply
}
}
+4 -4
View File
@@ -53,7 +53,7 @@ You are a **Technical Writer**, a documentation specialist who bridges the gap b
## 📋 Your Technical Deliverables
### High-Quality README Template
```markdown
````markdown
# Project Name
> One-sentence description of what this does and why it matters.
@@ -120,7 +120,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md)
## License
MIT © [Your Name](https://github.com/yourname)
```
````
### OpenAPI Documentation Example
```yaml
@@ -203,7 +203,7 @@ paths:
```
### Tutorial Structure Template
```markdown
````markdown
# Tutorial: [What They'll Build] in [Time Estimate]
**What you'll build**: A brief description of the end result with a screenshot or demo link.
@@ -255,7 +255,7 @@ You built a [description]. Here's what you learned:
- [Advanced tutorial: Add authentication](link)
- [Reference: Full API docs](link)
- [Example: Production-ready version](link)
```
````
### Docusaurus Configuration
```javascript
@@ -254,14 +254,32 @@ export function executeReorderTransaction(
): { updatedYaml: string; changedRange: [number, number] } {
const doc = parseDocument(yamlSource, { keepSourceTokens: true });
const seqPath = intent.targetSequencePointer.split('/').filter(Boolean);
const targetSeq = doc.getIn(seqPath);
if (doc.errors.length) throw new Error('Cannot reorder invalid YAML.');
const decodePointer = (pointer: string): string[] => {
if (pointer === '') return [];
if (!pointer.startsWith('/') || /~(?![01])/.test(pointer)) {
throw new Error('Invalid JSON pointer.');
}
return pointer.slice(1).split('/').map(part => part.replace(/~1/g, '/').replace(/~0/g, '~'));
};
const seqPath = decodePointer(intent.targetSequencePointer);
const sourcePath = decodePointer(intent.sourcePointer);
const indexToken = sourcePath.pop();
if (JSON.stringify(sourcePath) !== JSON.stringify(seqPath) || !/^(0|[1-9]\d*)$/.test(indexToken ?? '')) {
throw new Error('Source must be an item in the target sequence.');
}
const targetSeq = seqPath.length ? doc.getIn(seqPath) : doc.contents;
if (!isSeq(targetSeq)) {
throw new Error(`Target at pointer ${intent.targetSequencePointer} is not a valid sequence.`);
}
const sourceIndex = parseInt(intent.sourcePointer.split('/').pop() || '0', 10);
const sourceIndex = Number(indexToken);
if (!Number.isSafeInteger(sourceIndex) || sourceIndex >= targetSeq.items.length ||
!Number.isInteger(intent.targetIndex) || intent.targetIndex < 0 ||
intent.targetIndex >= targetSeq.items.length) {
throw new Error('Reorder indices must identify valid final sequence positions.');
}
const [movedNode] = targetSeq.items.splice(sourceIndex, 1);
targetSeq.items.splice(intent.targetIndex, 0, movedNode);
@@ -41,7 +41,8 @@ You are **Video Streaming Engineer**, an expert in delivering video that plays i
```bash
# Encode a multi-rung ladder with aligned keyframes (GOP) so ABR can switch
# cleanly at segment boundaries. Keyframe interval = segment duration * fps.
# cleanly at segment boundaries. Force 24fps before the 48-frame (2s) closed GOP.
# Keep comments on separate lines: a continuation backslash must end its line.
ffmpeg -i source.mov \
-filter_complex "[0:v]split=4[v1][v2][v3][v4]; \
[v1]scale=w=640:h=360[v360]; [v2]scale=w=1280:h=720[v720]; \
@@ -50,7 +51,7 @@ ffmpeg -i source.mov \
-map "[v720]" -c:v:1 libx264 -b:v:1 2800k -maxrate:1 2996k -bufsize:1 4200k \
-map "[v1080]" -c:v:2 libx264 -b:v:2 5000k -maxrate:2 5350k -bufsize:2 7500k \
-map "[v1440]" -c:v:3 libx264 -b:v:3 8000k -maxrate:3 8560k -bufsize:3 12000k \
-x264-params "keyint=48:min-keyint=48:scenecut=0" \ # closed GOP, 2s @ 24fps, aligned across rungs
-r 24 -flags +cgop -x264-params "keyint=48:min-keyint=48:scenecut=0" \
-map a:0 -c:a aac -b:a 128k \
-f null - # (real pipeline pipes to a CMAF packager; keyframe alignment is the point here)
@@ -178,7 +178,7 @@ def preprocess_audio(input_path: str, output_path: str) -> str:
def chunk_audio(input_path: str, chunk_dir: str,
chunk_duration: int = 1800, overlap: int = 30) -> list[str]:
chunk_duration: int = 1800, overlap: int = 30) -> list[dict]:
"""
Split long audio into overlapping chunks for model processing.
@@ -189,11 +189,17 @@ def chunk_audio(input_path: str, chunk_dir: str,
overlap: overlap window in seconds (default 30s)
"""
import math, os
if not math.isfinite(chunk_duration) or chunk_duration <= 0:
raise ValueError("chunk_duration must be finite and positive")
if not math.isfinite(overlap) or overlap < 0:
raise ValueError("overlap must be finite and nonnegative")
result = subprocess.run([
"ffprobe", "-v", "quiet", "-show_entries", "format=duration",
"-of", "default=noprint_wrappers=1:nokey=1", input_path
], capture_output=True, text=True, check=True)
total_duration = float(result.stdout.strip())
if not math.isfinite(total_duration) or total_duration <= 0:
raise ValueError("Audio duration must be finite and positive")
chunks = []
start = 0
@@ -205,10 +211,11 @@ def chunk_audio(input_path: str, chunk_dir: str,
out_path = f"{chunk_dir}/chunk_{chunk_index:04d}.wav"
subprocess.run([
"ffmpeg", "-y",
"-i", input_path,
"-ss", str(start),
"-to", str(end),
"-acodec", "copy",
"-i", input_path,
"-t", str(end - start),
"-map", "0:a:0", "-vn",
"-acodec", "pcm_s16le", "-ar", "16000", "-ac", "1",
out_path
], check=True, capture_output=True)
chunks.append({"path": out_path, "start_offset": start, "index": chunk_index})
@@ -106,7 +106,12 @@ const request = (options) => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(res.data);
} else {
reject({ code: res.statusCode, message: res.data.message || 'Request failed' });
// Error bodies may be empty, plain text, or a gateway HTML page.
// Do not throw inside the success callback and leave the Promise pending.
const message = res.data && typeof res.data === 'object' &&
typeof res.data.message === 'string' && res.data.message
? res.data.message : 'Request failed';
reject({ code: res.statusCode, message });
}
},
fail: (err) => {
@@ -64,6 +64,9 @@ class PIPELINE_OT_validate_assets(bpy.types.Operator):
def execute(self, context):
issues = []
if not context.selected_objects:
self.report({'WARNING'}, "Select assets before validation/export.")
return {'CANCELLED'}
for obj in context.selected_objects:
if obj.type != "MESH":
continue
@@ -74,8 +77,8 @@ class PIPELINE_OT_validate_assets(bpy.types.Operator):
if any(abs(s - 1.0) > 0.0001 for s in obj.scale):
issues.append(f"{obj.name}: unapplied scale")
if len(obj.material_slots) == 0:
issues.append(f"{obj.name}: missing material slot")
if not obj.material_slots or any(slot.material is None for slot in obj.material_slots):
issues.append(f"{obj.name}: missing assigned material")
if issues:
self.report({'WARNING'}, f"Validation found {len(issues)} issue(s). See system console.")
@@ -111,6 +114,10 @@ class PIPELINE_OT_export_selected(bpy.types.Operator):
bl_label = "Export Selected"
def execute(self, context):
# The export button must enforce the same gate as the validation button.
if bpy.ops.pipeline.validate_assets() != {'FINISHED'}:
self.report({'WARNING'}, "Export blocked: resolve asset validation findings.")
return {'CANCELLED'}
export_path = context.scene.pipeline_export_path
bpy.ops.export_scene.gltf(
filepath=export_path,
+42 -42
View File
@@ -1,7 +1,7 @@
# convert-outputs manifest v2 — one line per agent (its output across every tool), one per tool
# (non-agent files), one per contract. Platform-neutral hashes. Regenerate: scripts/test-convert-outputs.sh --update
agent 3d-scene-developer cb46371b7ee6fa5fabfc5da1094a2b1a552e4adc1a76fcd44b604c2e0c9fed27
agent accessibility-auditor 9e873dda6ef18f2e2f058008fb051f09b9b95d1c9285f1bbccb5d94e54ece890
agent accessibility-auditor f797a3000f39d0c91a1722c5e4004a6db7f1d665fd069ea9e8c16be32c9c8a13
agent account-strategist 474bd07c750f87c202f214dab515ab7fb9c82394c62b9d5752902d5eb7fbd8a2
agent accounts-payable-agent 5079310c10346460332645bdcaea34a303a8ee9e9092cbba930c1c9602bda3d3
agent ad-creative-strategist e288208090b93e5d5d9cb63412788b14f0c9aee796f8513458abb5496ff4b27f
@@ -16,11 +16,11 @@ agent ai-engineer d93416c98c7462beb379753a9416ed94e8c4b8f5f680a3f1b7fb5904e60f70
agent ai-generated-code-security-auditor 0e706372c85d1c491e33bd505dccdb5cdbb86ec244e3a5d0c3a4c89afe77976b
agent analytics-reporter 84bbf7a058e826b0f416556403353fea224432184a1bab1f230eb1b556387301
agent anthropologist c79922e2739ea11715729b4535e2b32f6c0d861cd1f12915bdfb4584eff05981
agent api-platform-engineer 339d88f0c427b19fc24b66024a3f6c7697ecbd768935731c2a167400662f4b69
agent api-tester 94352c1e8dae8b0ef010d1a11748bd83fb83806b681c415409b9f18b8f167412
agent api-platform-engineer 952d2e1552d5629fad4c5edef70d3531a4686daf3728450afeb9ac935eaaa3aa
agent api-tester fc5016240261651d68832db41c41fb218b6b91f2db6ad1d5a45ace361a1b0bef
agent app-store-optimizer 4b4f5e0cec69b402b824970817b23774b00f46e7ce0b3d2e3ebd3278cdfea002
agent application-security-engineer 4f40e4a8354f1bf4880ee94db59a08adae29b81ba941edc07f5820b752ebfaa3
agent ats-validator-architect 8d330b7ef5405660883e9eef2da00cae52b144077fa65ea9ac17c7c8112f6f13
agent application-security-engineer d12e4555fcacb2d12ea386c21708430ca0853986646c9b2939dd6c08c32ef264
agent ats-validator-architect d79845cb837f233257ea51e6542f6b86e67ea238cc25014d0eddaf5cd8accae6
agent automation-governance-architect 181383ad60f2f885b034b1dd3560e77c906eaf102540e601c5bd23008fe6d83c
agent autonomous-optimization-architect de8397b19bc17516e5ae41a9c55df9894ccfffe4eeb70382930875a60dbaa369
agent backend-architect fdabc3c9625b5852594169f017fd1f8e100af9bc5c2b2eb3728256f8da803295
@@ -28,8 +28,8 @@ agent baidu-seo-specialist 19aecfcdb5a828592a56c602182cd483d3445e684e85bbedefa62
agent behavioral-nudge-engine 64be8cb90de0fb15386ac43321141ddf572b9d034af4ffc3faf955947064ba09
agent bilibili-content-strategist c0bdf892f215a743b716713b0f037c042ffefa3acafffeb4111403a9d20d1b3f
agent bim-gis-specialist ea191ab15ab0b25dd3705e83842bf59b3e7019b143e54ac0a250538559de46de
agent blender-add-on-engineer 0bef2e8a241622429e07e9cdbf0ffe47ba3f00eea22bea33d340cb586a07343e
agent blockchain-security-auditor 8fa9dcb81ed6a9272fee39222e1d4923370a3ff56efea20bf0cb3ba6a7d2c085
agent blender-add-on-engineer 74e5fbb003867f27a0a17e24e3cfda6e1cd546d3e04e2310d521598e1a5de8d7
agent blockchain-security-auditor e60938bb97086821d2bd0a8cd42364a602755126f3d22e32b23aef29a25584be
agent book-co-author 459ff00b518d9f8f77ee765cb1b4e5f0b427ae733f1714a527350cbf99d7bedc
agent bookkeeper-controller 73acf3a92e347510f0e76089e2e9bb9899d4a6f1890648423ce075affb0909d6
agent brand-guardian b095aeda9a0de234c166ae18a2ff3535501d755eb77f83757583ba5c42a44dc5
@@ -44,7 +44,7 @@ agent china-market-localization-strategist 4e144930de0c803ae930981d6e3577f4fa035
agent china-network-engineer 406a6032cc7292f28fa46e69ed43bc7e35ec987d9ef8477377e92982cb0b95b3
agent civil-engineer e0e0505c9ccc978a7a18399d4e3cc37ee5961255d76e393c538d94d896f5fc2c
agent clinical-evidence-agent 1e33191bdad4e58e0c61476fdbf47c92833705df8809e138b713f16dd3711f0d
agent cloud-security-architect af68f7640a57e6f277e9453bb66ef5fc63a4b018e820b8146502f4e1b3e9bc88
agent cloud-security-architect c3723f4f18fdd5ae947dfe70a565965c48d364d937568013ee98f5c7f24abb6b
agent cms-developer 0884de84176b0f78f8a704b86a1a9effaf0bc8459bf771a08c0aa8e1f741e1d8
agent code-reviewer 43a29ff0f7e96405412d84c8a655f1f9947bdd2d26182ed579cedd8d2deb8d3d
agent codebase-archaeologist 56ffbf032397192d0b2c047a32c729b79214e056468f2de8812f4d598d6967cf
@@ -57,17 +57,17 @@ agent cultural-intelligence-strategist a2fcfd37a164bc8aef96eb048f527498392322222
agent customer-service 5f4798b742303ee10cc8b387845a05bfc649ff7787b2974e197ba2651354b1d2
agent customer-success-manager 2b07e9e2632ccdb0e49d9340bb1d1d54ceceefbd50a35f4361c8fb452894bcfc
agent data-consolidation-agent 1dd9d3c4c264ddee3fe2e4e7e86fe2473c99df505d83b48eb6e0daa75d8a300a
agent data-engineer 650387aec75a738801413d7b4d0d9347892072a5eda5cef1469c79338e8327dc
agent data-engineer 71403e6c6e8a4eeb8ed85f6e2a5c1e86a9bb92f5073d61ab5a821925c91c50c4
agent data-privacy-officer 6795a5dc408218f49614942bb0bbf2022d673eddd85251d6446083c4c8e84235
agent data-visualization-engineer 869c6d2bb3699de027b6baa2c7c5d8790515bb9bcb54188726d0ae1222dd2a42
agent database-optimizer 20c71a07f482837cc5e7b111563719babdf7248163347ec3bae8684089338119
agent database-reliability-engineer e1ef00f76f15c9b7cdee96587d4f09d8037514cd5ccf8fce5187cb157ed5c658
agent database-optimizer 6a83d16d4482fcc84c4517aae827ca0823a02c06c082ccb87fe2631eb60ce43a
agent database-reliability-engineer 1a57963c5bea5e2f9f9f51e67af536072915d6e7140cc10a308a6344156e01eb
agent deal-strategist 491bda620283290a8d2af0edaafc0b773128bcb319daafaeb0887049268a65d3
agent desktop-app-engineer 8346e9b37b3989dd9d3f1fc91b7093580476880919a881cef237012f1b5c7b2a
agent developer-advocate 59a813fc4c7b022e56a41f2f666140eea4fd71e286ff8a86bcc54d88ea719ca6
agent desktop-app-engineer c8769b70f4411efc8e7ea1898908fc9f92bd673746b363c2accddf554b5dcbfc
agent developer-advocate bae8b3a650efe5225d7d75affa59147af809e27d85244e56e7e5906fad160a80
agent developer-community-builder e523c237b0fe0380c3912b6b86bc722d8d0ab2ec622b22c3f3e22ff3a99ed97e
agent developer-tooling-engineer 8e976c62607576da7eaec81e5a6eb17a0ecc9b6e89528b57144c8b866a6531ca
agent devops-automator 45dd972e3438c2fa4b52178080ed0e58da285f9495af1e6194ac06bc51b8f607
agent devops-automator 9716ea2ff303718da6d6abbc8a234e78112d83ad2fc9f0e5399ef5451bfec55b
agent discovery-coach c6dd139e5e70d296504a2de80c2d2af06e53c884a00a379e7f74bb7b1cf8a3e1
agent document-generator 84e6ad2c86370deb62c0a86daff973f601788121fdb03b65d7f94d6bdf1afea6
agent douyin-strategist 9a36813e6496f9ae416fe60dcb744a8ce3961641362b35fbb0f76dd194d29f99
@@ -76,11 +76,11 @@ agent drupal-performance-engineer 772c9f1282e7776b3f28ab3a6b830a0260b6de4f814f91
agent drupal-shopping-cart-engineer 00b3e2a42434e2d6fd19f8055e61162ba503e3a71a63597891ec7b91710f385f
agent dx-engineer 2a966b23d47604b36dbbf2ce746ca69dab1a3be9116537fa0ef625346f1572d9
agent economy-designer 359a80ea6bbd1c10a23d9f0acfdc045a5ae77aaadbb38c7ecc2a08e2b0ee4cf3
agent email-intelligence-engineer ec8c5b0513e9163986f4682e15dd8e070d75b4413271abef52ef8a10106ae159
agent email-intelligence-engineer c58ae528450270db72b03bc4695e86e828e099fc061e27f0a039aa01de4cdde2
agent email-marketing-strategist a0f8db4f46c40da8ba1fa794159f1a4f3aaa9b6efee10169e08dd5bc62d514ab
agent embedded-firmware-engineer 984c2eccab39eeee9bedf51a9d3ca4d340b8e72d7ff5ccfcc3ca6ff39903a1b4
agent esg-sustainability-officer 075a8eb55e512c598ac92240b99f2f85cfa16f57e828b7bb336993adcdd38361
agent evidence-collector 7250c568cbf4d622e4010f0db0f05e512a0b45a804a401736ccad7b679317259
agent evidence-collector 56f713c696453f6e631a34dff50f82e1b7d81b609602ba8943ace590e96ef773
agent executive-summary-generator b4a4ceb9ba860e223076a0144df62b8a25a4528b948e06020185e93c264e240a
agent experiment-tracker c5d3f07b835b7e2e10aad874ede6e7a3cc365d1700aa70794014758dd9738865
agent fedramp-rmf-compliance-engineer 48b72603d607cb425e0a2f25246e1d5c3a308c02a48289122bb546a5366c57eb
@@ -89,7 +89,7 @@ agent feishu-integration-developer d59f8216b2de9409013b93934e43ad763e743303f3f78
agent filament-optimization-specialist 11cabb3ba1f5325078e9d6871f6e40f32af94ca6c91cadbc494b2606a8b511ab
agent finance-tracker d82f098a2cf90fdcb42743bcac76416b379b7b8f5255a4643fb7c04f7b2cef6f
agent financial-analyst 5bc290a7ee437845c11d20d0c81ece9549c7659c0d701acd6238013df4e47f7c
agent finops-engineer 8f749fd3d29a8d0891ea73fbcdde2702d2d229a2ce4c3017c188818957b9085f
agent finops-engineer 2fd4275f09a76b8183256228cfb06d3d7313aa1a6ab2ab1ce6a8913291a11623
agent focus-music-architect d5f7ad52721bc2557dde8cdeea661e742086ce328d49202051447c94c33f6b8e
agent fp-a-analyst 765d403f720dd6ff2951fe8347ab3bca89d2c671e3d4eb355ba4dcc1743d3957
agent french-consulting-market-navigator a776945e7ce36a0a807322c9897158a7ea6712b7571c319e0086c80aa58ec7e5
@@ -102,7 +102,7 @@ agent geographer 68dedff8020bf80668e3343e9c14dced87191f4a669dda23c4ee0d5d840053e
agent geoprocessing-specialist 4137f9deb327f880406b8ffc5fcf860f4c6ca49ea312735097004ad4eca203b9
agent gis-analyst 095a1e6436a7da61070874198ec48ce9711ac5269882510fe6d4af38c90774e9
agent gis-qa-engineer 130cdfad3a8091529eab10833d731bd0969b88f043268c6aad4b34cc3f4609dc
agent git-workflow-master ad8db6bd75a1183dddce5e28e329a117b84410ea9191300dc7b90d0189c1f819
agent git-workflow-master 6b22e6d1d39473b3a67cc8cbbb8b8238ec2184fa3f6605a75b1218cd01b17736
agent global-podcast-strategist 0a0d65a7971af7cd439c383c4ffaf7d43d89ae808756e47391e047b0fed68323
agent godot-gameplay-scripter 772157391d97f20a7fb02cc5008d9fc7e02871cda94a700bb328a6d3f352d3f9
agent godot-multiplayer-engineer 3ef11d3a5cae9f42f12210f0737cac0ff0d6f40e80aa2b9f4f52e5155ca82216
@@ -116,20 +116,20 @@ agent healthcare-marketing-compliance-specialist 5f2e709deff3066ee2e26a392118249
agent historian 883638a108ce343ca5cc06febcf4b99d35142c6fc0be3b6605c8865d6722dea1
agent hospitality-guest-services 503bf1b27fe2fb35e0a5e772d70943081c03093336fd32e157e9798f6b9fac87
agent hr-onboarding 55ec220679bc78de1f3b094b643534077d1beaf80f98b263145caa97f4614d6d
agent identity-access-engineer a225edd2a33ff902cddd776bc4bf25529296dfac0d5eddf25e70dfb73c359fcf
agent identity-access-engineer 8af7fadd60e08a8929ea1f19ad48611d5862f099b20b83aac0ecda0d7edf04bc
agent identity-graph-operator 565db70f50623d540e5af7883e2cbaff88715951e931693cde25d2e948680456
agent image-prompt-engineer 6efdec2f800dc7182db7380c6b9981591893d78808ddbb2d12b6b4991af6351e
agent incident-responder 426c5ba1c85d5ede51add9c16237836704a2bbf50c52c6327ca7358dacf6f989
agent incident-response-commander 6c9585e10a072baf8e43153e33dfdacd98cca4e9dd8b2d69cb1bc095b58aebfd
agent incident-responder 22680b884039638e991b64228dd6354e0617acd4b133bfed797ab157974fb521
agent incident-response-commander 5fdf424e9cb991d4baf922328a6f0926cc4e064ed2ecf2f987e0761f5a0d1346
agent inclusive-visuals-specialist 939821deb9f25ff9a14dcd18b3aee4036bdd00d0381c385b013ecaff1c9f236b
agent infrastructure-maintainer 00159f8d363b241319b65e3a5a3fc87b3effdd18912d9e3436dc84c80b17998b
agent instagram-curator 00a02db1ed88d56d456e91475a64c3f03e4cfdf3a0e9d19c031f9ece5328cf29
agent internationalization-engineer 93aa0c58c78f9233d41e51581ce213bd98a4286bd8d03220c42c8e42a5dda3e6
agent internationalization-engineer f39fce742bf04eb885adab011c4929b551c7de17a70e62a14ec454c615192719
agent investment-researcher 0cb358dbad201073d75f75da528a80c36aa4610674ce3e925d42905584bb4a2b
agent iot-fleet-engineer 0654db892579d6c0e796f53ec50ab04d784752ba0df9eb14de5eabe47b0569fc
agent it-service-manager fa31fc63dd9c23d9f5f2a497a0d3d51ce4c7bdd090d10de606a3ae28632a37b2
agent jira-workflow-steward 4cbba5f9ef76c5e25074c4204d3f6f86bbfff7ce4621b58b30e50266b0c8cb72
agent knowledge-graph-engineer a71705683177a20c6398d4da7dc2689e19d93b5af2e8f6b3b5ba7df8f3ccc2ba
agent knowledge-graph-engineer 18996402a347ff06fe4c84e4514994ae3a5ab2d38da2ec4ccb03affd61e73100
agent korean-business-navigator cbaef5cc37130363a28ff901a11bb20ee39dceee94c4bdfc0ae9fe6f38e6ef22
agent kuaishou-strategist fff4b32c0de544f47dbf1ce2329c70428399626288449de3d8318c428edc73d5
agent language-translator 1c5acef0ff5ac8299e991bf470d07ad9d9d4d9e920afcec4d74aa1b70091e51c
@@ -145,14 +145,14 @@ agent loan-officer-assistant 728dd25886c11a497cffbc15c9f0a5e9e2ad31d81dd0ab5d73c
agent lsp-index-engineer b85f51e05fc7417725859cf7db2e75c689cdc83e9a0834491a21bfe90bf56f3c
agent m-a-integration-manager 8b4fa0faafa68a917e74ea811d5a1dc498d96084339fc9e37bfb7cb83124f65e
agent macos-spatial-metal-engineer 459c44c093c14db20c3e26e11486faf1b51a2ed2f5ccf1cdbab73de17308ff61
agent master-plan-architect c861675824d79338d49f01dc04632bce755743a19ceefc02b4927b2bb17a7118
agent mcp-builder b43870f2ed7bf26d8f67d3d9e7bcc77d85f4e1c11c8c032e204fad34352aed29
agent master-plan-architect 181d5e2464dd1fb15e7951c2fff2c3b8feca97b1d066256ea9f26e27493c49e8
agent mcp-builder 436091e1d1e7701f41700efae56a36c27ceab7bcd517e0f1e951153c663e4797
agent medical-billing-coding-specialist 87637314fad64f8568209dcf95f00aac2374b9acddbb324b697d565029290cfd
agent meeting-notes-specialist 2abd4b0e4b5d84d36306b7a5a01d1cae280ac03183cbeb7dcb271b5890e5d172
agent minimal-change-engineer 3e712abbc8bf7e79ab4b7f955c95131bccd665efc064e439f47787dc7f27e23a
agent mobile-app-builder 6047ed573ead2b152ed01ec9d241a4e4a8fe60342aaee3213e6d21025f84afcb
agent mobile-release-engineer a36d473db28e070064d5f1ed75eff59cf36985a89b8985ee37bdc30ce47c0f24
agent model-qa-specialist bbfe2a36835d325c36863ba53362867f1f5ec13322f16176dafeb4ca42b5cd92
agent model-qa-specialist 416a9cdf19132984b8df2c905b1c4e98a0d0af9f9cad59a14aa7a2a786cf063d
agent multi-agent-systems-architect 85ebc3de09b5619380454411a1c910bebf3e382b67a8920a01cd4d488fd9143a
agent multi-platform-publisher 7190db08b2e29bed5b6df887d2cfa42143fc880f19bcf64f0064861cf22d05f4
agent narrative-designer 2e487cbeaf61832a899f63398e6453f9d44d181a56ebc44e5a042a7c1c302e88
@@ -165,10 +165,10 @@ agent orgscript-engineer a8cb7cea43d400f0c608aae00b4214419bdfc526f94075a55bd27dc
agent outbound-strategist 3e28d6e7507f0c0c7c8b58ee1240458241d9dc25949f8bf9e95b2b0fab6ca181
agent paid-media-auditor 2ae7e56aa2f9a8b06bab19f6c8560878aa2469afc96a125dcd31c0250515733a
agent paid-social-strategist 76157e6b683ab4072f42834ea7d7f3f66c834f504e9178802a0be5fd955d5eb9
agent payments-billing-engineer 8ff3a2d3ff29009cff13e6d9cfc770f67a76494831eb273f70388349e311a676
agent pdf-engine-architect d38fe55ffb390460728e7a0738326c46fbc6e4063e320619ba832f7f1d865f3a
agent payments-billing-engineer 92932901363c3d7e784bdef19e04a13faef5b2cd4173170910526e2bf434a578
agent pdf-engine-architect 04d7366e40373caa669a09fa8cf7c5327e48a122d1ff3e2f78b40f8a327fa0fd
agent penetration-tester 56d7934b013b2e11c4290d1316248c097122e092ebef5de76f62ed60b91ed5ba
agent performance-benchmarker 225559d2d912646ef5bf70f20fdf6c5a77aff2acb8f0226b0aa4e8e6c7577824
agent performance-benchmarker 408fe40fdc3bebb8b90a55b415aa9620abc7f446058010dc57bc0e7abe7a99e0
agent persona-walkthrough-specialist 27f17ab9289480e277cd51ed96a95e04fb23e3c9f2cfef300cc940af6ffc8d44
agent personal-growth-mentor 11a0955f8343b5ffb9c389e02c21e7101f6d21cea98ac294871c7590d51b1be4
agent pipeline-analyst d2a3af6fa137fc02ff9910976d708390546be2aba772b3c15072d11f1e4356e1
@@ -185,11 +185,11 @@ agent project-shepherd f366e437da8f1334948793965ff6de6b8bb00ca6a46814a2219346365
agent prompt-engineer 5100207414ee407bfcc99520edfcc4cf71205b357ee771d6ec41dd39de1b1a31
agent proposal-strategist 8586fd4d88a2dc4d865aed1a0b001772a6e414d85021ccae3b9493402d65024d
agent psychologist 2305460ad8b493f8a60759b7b736ff6fd7e02f87cca3c4c70e15e20b59f62bef
agent rag-pipeline-engineer f68905a75c93ecc2eb564a2adc3d68845ff04a22ce78ea3eb9ef1dd318393110
agent rag-pipeline-engineer ea18e79982814e7e2a68803fb0db9a28ded2e5ccfefca853a549069de163e093
agent rapid-prototyper 8f11bc93e1188cf78754862bdaa1540bc6c892e0ccc12e58bf6a91bdf86dd9a0
agent real-estate-buyer-seller 4f2077bedf36c6128157cb81cdbe0b9c15b02999622e593d971022ff9e5775de
agent reality-checker ac3f586d5ac39462ee5c3b89860dfeaddd3612544ac8367621a97973495fa226
agent realtime-collaboration-engineer d05ff200171c9f0e46a6d3fe70b09f986eb9bf98f06c6140ac0b160354e8bfaa
agent realtime-collaboration-engineer 6b730eab359407f375d8ad394f892174f9dddec963ed39f032828f4b0a5a2e56
agent recruitment-specialist 851b5cad87722e10e40e9f1d2581ad78f7bded0ff9dee951deb003c5049b8fd3
agent reddit-community-builder b8a189f0d247df6148ba046d942696cb34346a2359bf9b81d476b00e18e788ac
agent report-distribution-agent 43930d1b0eadf06f99ad03b5bc434e32025208c172aec62888b4442291d4347b
@@ -207,7 +207,7 @@ agent sales-outreach 625b0a5bfd7d0a0e2c3b85c0c3e3e8473da57c2e628b360a458e4095210
agent salesforce-architect ca346376db8042f68861affa71dd4cade7c8c7b791351e0c8be0e2dc6781c531
agent search-query-analyst fa26d7b5c018c13b3bec59887857a1a28c3c285ea419b963ce3dc43477af821e
agent search-relevance-engineer d6ed38705708af22fd4d1b6ec817e9f46c897553ab132f70742bc72d92cc0435
agent secrets-credential-hygiene-engineer 92cf9ecabc1cf2cdcc1aebe3c8e653c88f22051b8a5bc08975e32db638768a76
agent secrets-credential-hygiene-engineer 574645c70294f161642e0ba2cd2e5bc9aefcc6b7ddc0b4e20cf8a741b2203ba4
agent section-508-accessibility-specialist 1907cdc7cbd412401523a96fa940e86d9ee1966dc5e0cc38defefc901b11b392
agent security-architect 9b339a90ac4f3e26fc40a6ce32911ba8db62a15b56ff5d10a3fb90cc37d30186
agent senior-developer 697c44f1593cfa31b81a52596f01bd6d52170e1f8ba640a45c8fd668bed75b74
@@ -235,11 +235,11 @@ agent support-responder c7fee67f6eed4b16002326dc4355ba341ed540b1be5ba42c93215200
agent tax-strategist 4f0318fbf6f15f1041025fdf58deb0da1258e93b7c97e7387a82afc4fb2ba3a7
agent technical-artist 49b07b6ab626c06d7d5e4b1fc7f6e1f6794f100e7a03d2b79b0d041855073e39
agent technical-consultant 6b94aba5684f1f97dcc658ec023b7e646f88ed9257b6af642b7ad9f970e23000
agent technical-writer 358b7bb56afc0a78c84184896b9310d5bc15f0ca5793290099df3dd7afc39f5d
agent technical-writer 3d677d6f5a5c1ca2e3fa1ceefbce7e95c8a7812ec2345a972f4bd2bb6beca2cd
agent terminal-integration-specialist 77404afbbbf384d091e4ba1e86610d0a40260626992e1e910795d4a7143e852c
agent test-automation-engineer 1928169ccfcb12872e10f697824dc0b444ae695b889f2e3eda78ba4deda220e2
agent test-results-analyzer 9ff4ba39e67ed23c6dad0de0f4ed8c06425d3eaca5944123a10fe2973f4d20c9
agent threat-detection-engineer 2931ec321758ff5d4bf474ca512b2beed7c072a03356d3bce030fbd4e967f702
agent test-automation-engineer 8789e56e01c02beb34d15fb1b957965ba39961baedb50f303279c148d566ffee
agent test-results-analyzer 340668dc8782da6df908157d3cc63c7ebeb97e0b3f5224fbed9074d2c50cee13
agent threat-detection-engineer ce4188addbb8f4a32c3912bf98b59e9bc9a81f692ef0a4f9f89cfa8f5844499c
agent threat-intelligence-analyst 8137c504d3ba085151e4554e869696090544f710425c0155a36b3938e2c0d210
agent tiktok-strategist 7e0c79b1196e61545706236b429c5520627b4f5d9a04b369f56877fd6802483e
agent tool-evaluator 35be3ea9f0fe799c702225d7c09c1856eeb7909c95d9d0aeba4435b6ef0043dd
@@ -252,28 +252,28 @@ agent unity-architect a1ddee16a9f82641fc5ebbc56f33f982926d0b87138acabf3583f0f010
agent unity-editor-tool-developer 5204727b77a78916459207cd7d678a0496fea223329a123d99fa40ab667c7822
agent unity-multiplayer-engineer 260a7ccc1cd8f9bd9029f240da96ccd8b138a036b789ea61e736afae703d1753
agent unity-shader-graph-artist 8418d0e563195bb7e296cd4d6f9028b309bc295cf1f37fbf8e10acba8964c379
agent universal-document-compiler 7b8f80f17f5fe823be8727c1f71412ee9105f81e369b839230bf2ce2b8107f70
agent universal-document-compiler 969ab4e865cafd4545e522e911a05278ea5efa2bc882e1e974ebeb6bab0cc5f6
agent unreal-multiplayer-architect b891806c48fee99c08d715b7fb5e5c1151d570d48b3390ad9917b0033c18cad8
agent unreal-systems-engineer 57295f149a43fbd17a03793abbf91c29bc9b89079962598111a32ae6a5e75fab
agent unreal-technical-artist d3cf3189d85bd5843007fc7abcd8b03b34447c6e2fb663e925c1341399184af0
agent unreal-world-builder dfc9b1616e876387a5e9a1495d1c0de4b9001fb1082253fd7f076857e1a209b8
agent uswds-developer c231a0464daac1ca3693d32fbb45a9e47e92f1103411194f4e4950264442c509
agent ux-architect 94136fde96ddb23cb414b9108102dd5ebf2828f7faa731e37e747081d2a792ad
agent ux-architect b6dcbccd85dc212136d35ae4b3f3f1d1e01ff04979a6c7c34289d91e09292d10
agent ux-researcher f12ed95ef0a83912c5f8d6581b8770ca5506cf86de786d42f9b08d3a60773f67
agent video-optimization-specialist 692a27cc151aec0f9ca513c8fc6373d7281b588121f3491bf8f1c058727aaae0
agent video-streaming-engineer 584e612444dc48a2e769164fc2f6523e81a901054d58eeec1b3d0ff7ed0295fa
agent video-streaming-engineer 79e0bec23332a65b7d0827d27e195718cb1a14991c756a7357800f47f08a711d
agent visionos-spatial-engineer 725fec9ae38039d338453e5a9b6e47959d8046d29b2df9d14d49bd4ad08193c2
agent visual-storyteller 7f96d968a54df0304e2592c45d9e96c005abb085ba07dcbb7ef52f6da9ae341c
agent voice-ai-integration-engineer f5f37e76a990eec8165dc3036baa57ee26ab7e80ead1ab70653fce0b010633b1
agent voice-ai-integration-engineer 98e66bf00214c516183503fd5088ec230242fefc08b8ef00421ad988bb1016d0
agent web-gis-developer 03af81f34e7e835aaed3d40039e15bb06ce6f631dc65881af499a58b615714b5
agent webassembly-engineer 186ff15a1d2f9ea575d035953a382709b47d5ab4542a6cf497b31230adfb5f52
agent wechat-mini-program-developer b4ca454b1bb0d430029cf5fb33cc0cc663452c4bfd96aaf0ce1d578ec6dc134e
agent wechat-mini-program-developer f9864c9def86dc9f906710b81e929d65c4a636cb50dd6c6d669e109a35a4fb2e
agent wechat-official-account-manager 5225da2da8de3629d33516126f5baa2cf303ec6c36b23f00b6a5faa35afaa680
agent weibo-strategist 8b2ae550969b402047370fdcc67383c48e0039326811c02c204cdc0ca0066699
agent whimsy-injector 1f1d4ed575bdbe8d6f9878d062cffe7638b3ec25b2e6ca18660ac514b1dca093
agent wordpress-performance-engineer e5648f29545c04eb9fa59f7dd6ae486f1ead83518da35b7cd1ccac7a9cb851b6
agent wordpress-shopping-cart-engineer 55061d3f9d9e3b3326539964a1dc10e8dd9280988edfd470893e81e55f09ddf7
agent workflow-architect 3a23681bded4c9573a234dda8771bf6a596d08dd83e193122cc1e8bcd8670f95
agent workflow-architect f8ef420a2f211bea092c08f65946b3bd424933d990de29514c7ede6b07becd03
agent workflow-optimizer d9d871f40edb0bf57a36766422342f39d40662a55dedfc5160d5773f43adc8c3
agent x-twitter-intelligence-analyst 5aac652ec86bf81addde3271ef2f3c73f3dec31d8ef1acab85e6bb228331062f
agent xiaohongshu-specialist 51ba0e33a86db9a79515e8a77a36e9c67cf46528899c4f1bef3f70ccaec70639
+17 -3
View File
@@ -6,6 +6,7 @@
# converted files to integrations/<tool>/. Run this to regenerate all
# integration files after adding or modifying agents.
#
# --- USAGE-START --- (sentinel for usage(); do not remove)
# Usage:
# ./scripts/convert.sh [--tool <name>] [--out <dir>] [--parallel] [--jobs N] [--help]
#
@@ -30,8 +31,12 @@
# Output is written to integrations/<tool>/ relative to the repo root.
# This script never touches user config dirs — see install.sh for that.
#
# --tool <name> Convert for one tool (default: all).
# --out <dir> Write to <dir>/<tool>/ instead of integrations/<tool>/.
# --parallel When tool is 'all', run independent tools in parallel (output order may vary).
# --jobs N Max parallel jobs when using --parallel (default: nproc or 4).
#
# --- USAGE-END --- (sentinel for usage(); do not remove)
set -euo pipefail
@@ -77,9 +82,18 @@ AGENT_DIRS=(
)
# --- Usage ---
# usage [status] — print the header between the USAGE sentinels and exit.
# `--help` exits 0 on stdout; an unknown option exits 1 with the text on
# stderr. The old hard-coded `sed -n '3,28p'` stopped above --parallel,
# --jobs and --out, and exiting 0 after "Unknown option" meant a mistyped
# flag in CI or a wrapper script read as success.
usage() {
sed -n '3,28p' "$0" | sed 's/^# \{0,1\}//'
exit 0
local status="${1:-0}"
local text
text="$(sed -n '/^# --- USAGE-START ---/,/^# --- USAGE-END ---/p' "$0" \
| sed -e '1d;$d' -e 's/^# \{0,1\}//')"
if (( status == 0 )); then printf '%s\n' "$text"; else printf '%s\n' "$text" >&2; fi
exit "$status"
}
# Default parallel job count (nproc on Linux; sysctl on macOS when nproc missing)
@@ -793,7 +807,7 @@ main() {
--parallel) use_parallel=true; shift ;;
--jobs) parallel_jobs="${2:?'--jobs requires a value'}"; shift 2 ;;
--help|-h) usage ;;
*) error "Unknown option: $1"; usage ;;
*) error "Unknown option: $1"; usage 1 ;;
esac
done
+122 -48
View File
@@ -33,8 +33,9 @@
# Selection (compose freely; empty = everything):
# --tool <a,b> Only these tools
# --division <a,b> Only these teams/divisions (comma-separated)
# --agent <slug,slug> Only these specific agents
# --agents-file <path> Agents listed in a file (one slug/name per line, # comments ok)
# --agent <id,id> Only these specific agents (install slug, display name,
# or file stem such as engineering-frontend-developer)
# --agents-file <path> Agents listed in a file (one id per line, # comments ok)
#
# Mode:
# --link Symlink instead of copy (updates propagate)
@@ -165,6 +166,7 @@ AGENTS_FILE="" # --agents-file
DRY_RUN=false # --dry-run
SELECTION_ACTIVE=false # true once any agent-level filter is applied
_ALLOWED_SLUGS="" # newline-delimited cache of allowed slugs
_ROSTER_INDEX="" # "<install slug>\t<file stem>" per agent; see roster_index
# division_files <division> — agent file paths (frontmatter only) in a division.
division_files() {
@@ -178,16 +180,43 @@ division_files() {
# division_count <division> — number of agents in a division.
division_count() { division_files "$1" | grep -c . ; }
# agent_slug_exists <slug> — verify a requested agent against the source roster.
# Selection filters should fail before installation when they name nothing that
# can be installed; otherwise dry-run counts and completion messages lie.
agent_slug_exists() {
local target="$1" div f
# roster_index — fill _ROSTER_INDEX with one "<install slug>\t<file stem>" line
# per agent, once. Call it in the parent shell before resolve_agent: a $(...)
# caller would build its own copy and throw it away.
#
# Resolving each requested agent used to rescan the roster, running get_field
# on all 279 files per request, so a 36-agent runbook roster cost ~10,000
# get_field calls before anything installed.
roster_index() {
[[ -n "$_ROSTER_INDEX" ]] && return 0
local div f
for div in "${ALL_DIVISIONS[@]}"; do
while IFS= read -r f; do
[[ "$(agent_slug "$f")" == "$target" ]] && return 0
_ROSTER_INDEX+="$(agent_slug "$f")"$'\t'"$(basename "$f" .md)"$'\n'
done < <(division_files "$div")
done
}
# resolve_agent <requested> — print the install slug for a requested agent,
# 1 if nothing matches. Selection filters should fail before installation when
# they name nothing that can be installed; otherwise dry-run counts and
# completion messages lie.
#
# Two spellings name an agent. The install slug comes from `name:` and is what
# --list agents prints. The file stem is the corpus id strategy/runbooks.json
# uses ("engineering-frontend-developer"), and for 206 of 279 agents it is not
# the slug, so 35 of the 36 agents the runbooks list could not be selected by
# the ids the runbooks give. Slugs are tried first; no stem equals another
# agent's slug today, and slug-first keeps it unambiguous if one ever does.
resolve_agent() {
local target="$1" slug stem
[[ -n "$target" ]] || return 1
while IFS=$'\t' read -r slug stem; do
[[ -n "$slug" && "$slug" == "$target" ]] && { printf '%s' "$slug"; return 0; }
done <<< "$_ROSTER_INDEX"
while IFS=$'\t' read -r slug stem; do
[[ -n "$slug" && "$stem" == "$target" ]] && { printf '%s' "$slug"; return 0; }
done <<< "$_ROSTER_INDEX"
return 1
}
@@ -199,7 +228,8 @@ build_selection() {
return
fi
SELECTION_ACTIVE=true
local slugs="" div f s line requested
local slugs="" div f s line requested resolved
roster_index
for div in ${FILTER_DIVISIONS[@]+"${FILTER_DIVISIONS[@]}"}; do
while IFS= read -r f; do
s="$(agent_slug "$f")"; [[ -n "$s" ]] && slugs+="$s"$'\n'
@@ -207,11 +237,11 @@ build_selection() {
done
for s in ${FILTER_AGENTS[@]+"${FILTER_AGENTS[@]}"}; do
requested="$(slugify "$s")"
if ! agent_slug_exists "$requested"; then
if ! resolved="$(resolve_agent "$requested")"; then
err "Unknown agent '$s'. Use --list agents to see the available roster."
exit 1
fi
slugs+="$requested"$'\n'
slugs+="$resolved"$'\n'
done
if [[ -n "$AGENTS_FILE" ]]; then
[[ -f "$AGENTS_FILE" ]] || { err "agents-file not found: $AGENTS_FILE"; exit 1; }
@@ -220,11 +250,11 @@ build_selection() {
line="$(printf '%s' "$line" | xargs 2>/dev/null)" # trim
[[ -z "$line" ]] && continue
requested="$(slugify "$line")"
if ! agent_slug_exists "$requested"; then
if ! resolved="$(resolve_agent "$requested")"; then
err "Unknown agent '$line' in agents-file '$AGENTS_FILE'."
exit 1
fi
slugs+="$requested"$'\n'
slugs+="$resolved"$'\n'
done < "$AGENTS_FILE"
fi
_ALLOWED_SLUGS="$(printf '%s' "$slugs" | sort -u | sed '/^$/d')"
@@ -283,26 +313,30 @@ OVERRIDE_PATH="" # --path (single-destination override)
# install_file <src> <dest> — copy, or symlink when --link is set.
install_file() {
if $USE_LINK; then
ln -sf "$1" "$2"
else
local target="$2"
[[ -d "$target" ]] && target="${target%/}/$(basename "$1")"
if [[ -L "$target" ]]; then
# cp would follow the link and overwrite whatever it points at.
local link_to; link_to="$(readlink "$target")"
if [[ "$link_to" == "$REPO_ROOT/"* ]]; then
# Our own --link install: switching to a copy is the intended change.
rm -f -- "$target"
else
# Someone else's link: leave it and its target alone, keep installing
# the rest, and say so in the summary (one stray link must not abort
# the install halfway through the roster).
warn "Skipped $target — it is a symlink to $link_to; not overwriting it."
[[ -n "${SKIPPED_LOG:-}" ]] && printf '%s -> %s\n' "$target" "$link_to" >> "$SKIPPED_LOG"
return 0
fi
local target="$2"
# Directory destinations have a trailing slash. Do not follow a leaf
# symlink to a directory when deciding which file belongs to the installer.
if [[ "$target" == */ ]] || { ! $USE_LINK && [[ -d "$target" ]]; }; then
target="${target%/}/$(basename "$1")"
fi
if [[ -L "$target" ]]; then
local link_to; link_to="$(readlink "$target")"
if [[ "$link_to" == "$REPO_ROOT/"* ]]; then
# An installer-owned link may be refreshed or switched to a copy.
rm -f -- "$target"
else
warn "Skipped $target — it is a symlink to $link_to; not overwriting it."
[[ -n "${SKIPPED_LOG:-}" ]] && printf '%s -> %s\n' "$target" "$link_to" >> "$SKIPPED_LOG"
return 0
fi
elif $USE_LINK && [[ -e "$target" ]]; then
warn "Skipped $target — it already exists; not replacing it with a symlink."
[[ -n "${SKIPPED_LOG:-}" ]] && printf '%s (existing file)\n' "$target" >> "$SKIPPED_LOG"
return 0
fi
if $USE_LINK; then
ln -s "$1" "$target"
else
cp "$1" "$2"
fi
}
@@ -311,12 +345,20 @@ install_file() {
# path_collision_group <tool> — tools in the same group write identical
# filenames into a shared --path and would overwrite each other; empty means
# the tool's output is distinct and may share a path with anything. Derived by
# installing one agent with every tool into a sandbox and comparing what
# landed; re-measure if a converter's output naming changes.
# installing agents with every tool into a sandbox and comparing what landed;
# re-measure if a converter's output naming changes.
#
# claude-code and copilot copy the source file under its own name. For most
# agents that is <division>-<slug>.md, but 73 of 279 are named <slug>.md
# already (all of game-development/, most of specialized/), and for those the
# name is exactly what gemini-cli, opencode, qwen and zcode write. Measuring
# with one engineering agent missed that, so `--tool claude-code,qwen --path X`
# reported both installs OK while qwen overwrote the Claude Code file. One
# group, because a full install collides on 73 files, not zero.
path_collision_group() {
case "$1" in
claude-code|copilot) printf 'raw-source-md' ;; # <division>-<slug>.md
gemini-cli|opencode|qwen|zcode) printf 'slug-md' ;; # <slug>.md
claude-code|copilot|gemini-cli|opencode|qwen|zcode)
printf 'agent-md' ;; # <slug>.md, or the source's name
antigravity|osaurus|dsh) printf 'agency-skill' ;; # agency-<slug>/SKILL.md
*) printf '' ;;
esac
@@ -456,9 +498,14 @@ usage() {
# (excluding the sentinel lines themselves) and strip the leading "# ".
# Using sentinels instead of hard-coded line numbers means adding lines
# to the header comment block won't silently break --help output.
sed -n '/^# --- USAGE-START ---/,/^# --- USAGE-END ---/p' "$0" \
| sed -e '1d;$d' -e 's/^# \{0,1\}//'
exit 0
# An unknown option passes 1: the text goes to stderr and the exit is
# non-zero, so a mistyped flag in CI or a wrapper script is not a success.
local status="${1:-0}"
local text
text="$(sed -n '/^# --- USAGE-START ---/,/^# --- USAGE-END ---/p' "$0" \
| sed -e '1d;$d' -e 's/^# \{0,1\}//')"
if (( status == 0 )); then printf '%s\n' "$text"; else printf '%s\n' "$text" >&2; fi
exit "$status"
}
# Default parallel job count (nproc on Linux; sysctl on macOS when nproc missing)
@@ -1553,7 +1600,7 @@ main() {
--parallel) use_parallel=true; shift ;;
--jobs) parallel_jobs="${2:?'--jobs requires a value'}"; shift 2 ;;
--help|-h) usage ;;
*) err "Unknown option: $1"; usage ;;
*) err "Unknown option: $1"; usage 1 ;;
esac
done
@@ -1676,18 +1723,20 @@ main() {
fi
printf "\n"
local installed=0 t i=0
local installed=0 t i=0 rc
local failed=()
if $use_parallel; then
local install_out_dir
local install_out_dir install_status=0
install_out_dir="$(mktemp -d)"
export AGENCY_INSTALL_OUT_DIR="$install_out_dir"
export AGENCY_INSTALL_SCRIPT="$SCRIPT_DIR/install.sh"
export AGENCY_INSTALL_EXTRA="$(worker_flags)"
printf '%s\n' "${SELECTED_TOOLS[@]}" | xargs -P "$parallel_jobs" -I {} sh -c 'AGENCY_INSTALL_WORKER=1 "$AGENCY_INSTALL_SCRIPT" --tool "{}" --no-interactive $AGENCY_INSTALL_EXTRA > "$AGENCY_INSTALL_OUT_DIR/{}" 2>&1'
printf '%s\n' "${SELECTED_TOOLS[@]}" | xargs -P "$parallel_jobs" -I {} sh -c 'AGENCY_INSTALL_WORKER=1 "$AGENCY_INSTALL_SCRIPT" --tool "{}" --no-interactive $AGENCY_INSTALL_EXTRA > "$AGENCY_INSTALL_OUT_DIR/{}" 2>&1' || install_status=$?
for t in "${SELECTED_TOOLS[@]}"; do
[[ -f "$install_out_dir/$t" ]] && cat "$install_out_dir/$t"
done
rm -rf "$install_out_dir"
[[ "$install_status" -eq 0 ]] || return "$install_status"
installed=$n_selected
else
for t in "${SELECTED_TOOLS[@]}"; do
@@ -1695,25 +1744,50 @@ main() {
progress_bar "$i" "$n_selected"
printf "\n"
printf " ${C_DIM}[%s/%s]${C_RESET} %s\n" "$i" "$n_selected" "$t"
install_tool "$t"
(( installed++ )) || true
# One tool failing must not cost the tools after it. A bare
# install_tool under set -e exited the whole script at the first
# `return 1`, so a missing integrations/cursor meant qwen, codex and
# every later tool were never tried and nothing said so.
#
# Not `install_tool "$t" || ...`: bash ignores errexit inside anything
# run on the left of || (subshell included), so a failing cp inside a
# tool would carry on as if it had worked. The subshell turns errexit
# back on for itself while the parent's is off for this one command.
set +e
( set -e; install_tool "$t" )
rc=$?
set -e
if (( rc == 0 )); then
(( installed++ )) || true
else
failed+=("$t")
fi
done
fi
# Done box
local msg=" Done! Installed $installed tool(s)."
(( ${#failed[@]} )) && msg=" Installed $installed of $n_selected tool(s)."
printf "\n"
box_top
box_row "${C_GREEN}${C_BOLD}${msg}${C_RESET}"
if (( ${#failed[@]} )); then
box_row "${C_YELLOW}${C_BOLD}${msg}${C_RESET}"
else
box_row "${C_GREEN}${C_BOLD}${msg}${C_RESET}"
fi
box_bot
printf "\n"
if [[ -s "$SKIPPED_LOG" ]]; then
warn "Not installed: $(wc -l < "$SKIPPED_LOG" | tr -d ' ') file(s) whose destination is a symlink to somewhere else:"
warn "Not installed: $(wc -l < "$SKIPPED_LOG" | tr -d ' ') file(s) whose destination is an existing user file or foreign symlink:"
sed 's/^/ /' "$SKIPPED_LOG" >&2
warn "Remove or replace those links, then re-run to install them."
warn "Move or remove those destinations, then re-run to install them."
fi
dim " Run ./scripts/convert.sh to regenerate after adding or editing agents."
printf "\n"
if (( ${#failed[@]} )); then
err "Failed: ${failed[*]} — see the [ERR] line under each above. The other tools installed."
exit 1
fi
}
main "$@"
+12 -2
View File
@@ -77,17 +77,27 @@ fence_open_p() {
}
# fence_closes_p <line> <open_marker> <open_len> <open_indent> — 0 if <line>
# closes the open fence (same char, run len >= open, indent <= open); 1
# otherwise, including non-fence lines (callers need not pre-classify).
# closes the open fence (same char, run len >= open, indent <= open, nothing
# but whitespace after the run); 1 otherwise, including non-fence lines
# (callers need not pre-classify).
#
# The "nothing after the run" part is CommonMark's rule, and GitHub renders by
# it: inside an open ``` block, a "```python" line is content, not a closer
# and not a nested opener. Accepting it as a closer made these helpers read a
# ```markdown template holding a ```bash example as two short blocks, while
# GitHub saw one block that closed at the example's bare ``` — so a ## line
# the split treated as code rendered as a heading, and the reverse.
fence_closes_p() {
local line="$1" open_marker="$2" open_len="$3" open_indent="$4"
local re='^( {0,3})(`{3,}|~{3,})'
[[ "$line" =~ $re ]] || return 1
local close_indent=${#BASH_REMATCH[1]}
local close_run="${BASH_REMATCH[2]}"
local rest="${line:${#BASH_REMATCH[0]}}"
[[ "${close_run:0:1}" == "$open_marker" ]] || return 1
(( ${#close_run} >= open_len )) || return 1
(( close_indent <= open_indent )) || return 1
[[ -z "${rest//[[:space:]]/}" ]] || return 1
return 0
}
+38 -3
View File
@@ -112,6 +112,15 @@ lint_file() {
return
fi
# The lightweight converters read plain or quoted scalar fields. A folded
# block otherwise passes presence checks but leaks its YAML indicator into
# the generated value.
if grep -qE '^[[:space:]]*[[:alnum:]_-]+:[[:space:]]*>([-+][1-9]?|[1-9][-+]?)?([[:space:]]+#.*)?[[:space:]]*$' <<<"$frontmatter"; then
echo "ERROR $file: folded YAML frontmatter is unsupported — use a single-line scalar"
errors=$((errors + 1))
return
fi
# 2. Check required frontmatter fields
for field in "${REQUIRED_FRONTMATTER[@]}"; do
if ! grep -qE -- "^${field}:" <<<"$frontmatter"; then
@@ -162,8 +171,15 @@ lint_file() {
local soul_headers=0
local agents_headers=0
local fence_marker="" fence_len=0 fence_indent=0
while IFS= read -r line; do
local fence_marker="" fence_len=0 fence_indent=0 fence_line=0
# Walk the body from the file itself rather than from $body, so fence
# errors can name a real line number. ($body drops every "---" line.)
local lineno
lineno=$(awk 'NR > 1 && $0 == "---" {print NR; exit}' "$file")
# "|| [[ -n $line ]]" keeps a last line that has no trailing newline; several
# agents end on a closing fence with none, and dropping it reads as unclosed.
while IFS= read -r line || [[ -n "$line" ]]; do
lineno=$((lineno + 1))
# Skip fenced code blocks so ## doc-comment lines (e.g. GDScript `##`)
# and in-fence markdown headers aren't miscounted (issue #849).
if [[ -n "$fence_marker" ]]; then
@@ -171,6 +187,17 @@ lint_file() {
fence_marker=""
fence_len=0
fence_indent=0
elif fence_open_p "$line" \
&& [[ "${BASH_REMATCH[2]:0:1}" == "$fence_marker" ]] \
&& (( ${#BASH_REMATCH[2]} >= fence_len )); then
# A fence line long enough to close this block that did not close it
# can only be one with an info string: someone nesting ```bash inside
# a ```markdown template. Markdown has no nesting at equal length —
# GitHub shows that line as text and ends the outer block at the next
# bare ```, so the rest of the template renders as a document.
echo "ERROR $file:$lineno: '${line}' inside the block opened at line $fence_line does not nest — the next bare ${fence_marker}${fence_marker}${fence_marker} closes the outer block instead"
echo " fence the outer block with a longer run (e.g. ${fence_marker}${fence_marker}${fence_marker}${fence_marker}markdown ... ${fence_marker}${fence_marker}${fence_marker}${fence_marker}) so the inner ones stay inside it"
errors=$((errors + 1))
fi
continue
fi
@@ -178,6 +205,7 @@ lint_file() {
fence_marker="${BASH_REMATCH[2]:0:1}"
fence_len=${#BASH_REMATCH[2]}
fence_indent=${#BASH_REMATCH[1]}
fence_line=$lineno
continue
fi
if [[ "$line" =~ ^##[[:space:]] ]]; then
@@ -191,7 +219,14 @@ lint_file() {
agents_headers=$((agents_headers + 1))
fi
fi
done <<< "$body"
done < <(tail -n +"$((lineno + 1))" "$file")
# An unclosed block runs to the end of the file: on GitHub, and in every
# tool the converters feed, everything after the opener renders as code.
if [[ -n "$fence_marker" ]]; then
echo "ERROR $file:$fence_line: code block is never closed — everything after line $fence_line renders as code"
errors=$((errors + 1))
fi
if [[ $soul_headers -eq 0 ]]; then
echo "WARN $file: no section headers map to SOUL.md in convert.sh"
+30 -1
View File
@@ -41,4 +41,33 @@ output="$($INSTALLER --tool claude-code --agent 'Developer Tooling Engineer' --d
exit 1
}
echo "PASS: install.sh rejects unknown agent selections and accepts display names"
# The file stem is the id strategy/runbooks.json uses, and for most agents it is
# not the install slug (engineering-frontend-developer vs frontend-developer).
output="$("$INSTALLER" --tool claude-code --agent engineering-frontend-developer --dry-run 2>&1)"
[[ "$output" == *"Agents: 1"* ]] || {
printf 'File-stem selection did not resolve to one agent:\n%s\n' "$output" >&2
exit 1
}
# Every runbook roster, fed to --agents-file as the runbooks list it, resolves
# to exactly its own agents. On main 35 of the 36 ids were "Unknown agent".
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
while IFS=$'\t' read -r runbook count ids; do
printf '%s\n' $ids > "$AGENTS_FILE"
set +e
output="$("$INSTALLER" --tool claude-code --agents-file "$AGENTS_FILE" --dry-run 2>&1)"
status=$?
set -e
[[ "$status" -eq 0 && "$output" == *"Agents: $count"* ]] || {
printf 'Runbook %s roster (%s agents) did not resolve:\n%s\n' "$runbook" "$count" "$output" >&2
exit 1
}
done < <(python3 - "$REPO_ROOT/strategy/runbooks.json" <<'PY'
import json, sys
for rb in json.load(open(sys.argv[1], encoding="utf-8"))["runbooks"]:
ids = sorted({a for group in rb["roster"] for a in group["agents"]})
print(f'{rb["slug"]}\t{len(ids)}\t{" ".join(ids)}')
PY
)
echo "PASS: install.sh rejects unknown agent selections and accepts display names, file stems, and runbook rosters"
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# convert.sh and install.sh: --help documents every option and exits 0; an
# unknown option exits non-zero with the usage text on stderr.
#
# Both scripts used to call usage() after "Unknown option", and usage() always
# exited 0, so `convert.sh --tol codex` in CI or a wrapper read as success.
# convert.sh's --help also printed a hard-coded line range that stopped above
# --parallel, --jobs and --out.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
tmp="$(mktemp -d "${TMPDIR:-/tmp}/agency-cli-usage.XXXXXX")"
trap 'rm -rf "$tmp"' EXIT
fail() { echo "FAIL: $*" >&2; exit 1; }
for script in convert.sh install.sh; do
status=0
bash "$SCRIPT_DIR/$script" --no-such-flag > "$tmp/out" 2> "$tmp/err" || status=$?
[[ "$status" -ne 0 ]] || fail "$script --no-such-flag exited 0"
grep -q 'Unknown option: --no-such-flag' "$tmp/err" || fail "$script did not name the unknown option on stderr"
grep -q 'Usage:' "$tmp/err" || fail "$script did not print usage on stderr for an unknown option"
[[ ! -s "$tmp/out" ]] || fail "$script wrote to stdout for an unknown option"
bash "$SCRIPT_DIR/$script" --help > "$tmp/help" 2>&1 || fail "$script --help exited non-zero"
grep -q 'Usage:' "$tmp/help" || fail "$script --help printed no usage"
! grep -q 'USAGE-START\|USAGE-END' "$tmp/help" || fail "$script --help printed its sentinel lines"
done
for opt in --tool --out --parallel --jobs; do
grep -q -- "^ $opt " <(bash "$SCRIPT_DIR/convert.sh" --help) \
|| fail "convert.sh --help does not describe $opt"
done
echo "PASS: unknown options exit non-zero with usage on stderr; --help documents every convert.sh option"
+4 -3
View File
@@ -330,7 +330,7 @@ elif not colour_bad:
# block in half and leaves each file holding a dangling fence, which renders as
# broken markdown for every user of that integration (#849). So every fenced
# block in a source must land intact in exactly one of the two files.
SPLIT_FENCE = re.compile(r"^(`{3,}|~{3,})")
SPLIT_FENCE = re.compile(r"^(`{3,}|~{3,})(.*)$")
def body_lines(text):
"""Mirror lib.sh's get_body, including `$(...)`'s trailing-newline strip."""
@@ -352,10 +352,11 @@ def fence_blocks(lines):
m = SPLIT_FENCE.match(line)
if not m:
continue
tok = m.group(1)
tok, rest = m.group(1), m.group(2)
if not marker:
marker, mlen, start = tok[0], len(tok), i
elif tok[0] == marker and len(tok) >= mlen:
elif tok[0] == marker and len(tok) >= mlen and not rest.strip():
# only a bare run closes: "```bash" inside a block is content (lib.sh fence_closes_p)
res.append((start, i)); marker, mlen, start = "", 0, None
if marker and start is not None:
res.append((start, len(lines) - 1))
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
scratch="$(mktemp -d)"
trap 'rm -rf "$scratch"' EXIT
for indicator in '>' '>-' '>+' '>2' '>2-' '>-2' '>+2' '>2+ # comment'; do
printf '%s\n' '---' 'name: Example Agent' "description: $indicator" ' First line' ' Second line' 'color: blue' '---' '## Identity' '## Core Mission' '## Critical Rules' > "$scratch/agent.md"
if bash "$SCRIPT_DIR/lint-agents.sh" "$scratch/agent.md" > "$scratch/result.log" 2>&1; then
echo "FAIL: linter accepted folded frontmatter $indicator" >&2; exit 1
fi
grep -q 'folded YAML frontmatter is unsupported' "$scratch/result.log"
done
for description in '"A > B"' "'A > B'" 'Ordinary description'; do
printf '%s\n' '---' 'name: Example Agent' "description: $description" 'color: blue' '---' '## Identity' '## Core Mission' '## Critical Rules' '> Body quotation' > "$scratch/agent.md"
bash "$SCRIPT_DIR/lint-agents.sh" "$scratch/agent.md" > "$scratch/result.log" 2>&1
done
echo 'PASS: folded frontmatter is rejected while quoted scalars and body quotations pass'
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
# One tool failing in a sequential install must not cost the tools after it,
# and the install must still exit non-zero and name the tool that failed.
#
# Before: install_tool ran bare under set -e, so the first tool whose output
# was missing exited the script. With gemini-cli, cursor and qwen selected and
# no integrations/cursor, gemini-cli installed, cursor printed its [ERR], and
# qwen was never attempted and never mentioned.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
tmp="$(mktemp -d "${TMPDIR:-/tmp}/agency-continue.XXXXXX")"
trap 'rm -rf "$tmp"' EXIT
fail() { echo "FAIL: $*" >&2; sed 's/^/ /' "$tmp/output" >&2; exit 1; }
mkdir -p "$tmp/repo/scripts" "$tmp/repo/engineering" \
"$tmp/repo/integrations/gemini-cli/agents" "$tmp/repo/integrations/qwen/agents" \
"$tmp/home/project"
cp "$SCRIPT_DIR/install.sh" "$SCRIPT_DIR/lib.sh" "$tmp/repo/scripts/"
cat > "$tmp/repo/divisions.json" <<'EOF'
{
"divisions": {
"engineering": {}
}
}
EOF
cat > "$tmp/repo/engineering/agent.md" <<'EOF'
---
name: Sample Agent
description: Example agent
---
Instructions.
EOF
printf 'Gemini output\n' > "$tmp/repo/integrations/gemini-cli/agents/sample-agent.md"
printf 'Qwen output\n' > "$tmp/repo/integrations/qwen/agents/sample-agent.md"
# integrations/cursor is deliberately absent: cursor is the tool that fails.
status=0
(cd "$tmp/home/project" && HOME="$tmp/home" PATH=/usr/bin:/bin \
bash "$tmp/repo/scripts/install.sh" --no-interactive --no-convert \
--tool gemini-cli,cursor,qwen > "$tmp/output" 2>&1) || status=$?
[[ "$status" -ne 0 ]] || fail "install exited 0 although cursor failed"
[[ -f "$tmp/home/.gemini/agents/sample-agent.md" ]] || fail "gemini-cli, before the failure, did not install"
[[ -f "$tmp/home/project/.qwen/agents/sample-agent.md" ]] \
|| fail "qwen, after the failing cursor, was never installed"
grep -q 'integrations/cursor missing' "$tmp/output" || fail "cursor's own error was not shown"
grep -q 'Failed: cursor' "$tmp/output" || fail "the summary does not name the failed tool"
grep -q 'Installed 2 of 3 tool(s)' "$tmp/output" || fail "the summary does not count the tools that installed"
echo "PASS: a failing tool is reported and the tools after it still install"
# The clean path is unchanged: every tool installs and the install exits 0.
mkdir -p "$tmp/repo/integrations/cursor/rules" "$tmp/home2/project"
printf -- '---\ndescription: x\n---\nCursor output\n' > "$tmp/repo/integrations/cursor/rules/sample-agent.mdc"
(cd "$tmp/home2/project" && HOME="$tmp/home2" PATH=/usr/bin:/bin \
bash "$tmp/repo/scripts/install.sh" --no-interactive --no-convert \
--tool gemini-cli,cursor,qwen > "$tmp/output" 2>&1) || fail "a clean install exited non-zero"
grep -q 'Done! Installed 3 tool(s)' "$tmp/output" || fail "a clean install did not report all three tools"
echo "PASS: a clean multi-tool install still exits 0"
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
scratch="$(mktemp -d)"
trap 'rm -rf "$scratch"' EXIT
mkdir -p "$scratch/repo/scripts" "$scratch/repo/engineering" "$scratch/repo/integrations" "$scratch/home"
cp "$SCRIPT_DIR/install.sh" "$SCRIPT_DIR/lib.sh" "$scratch/repo/scripts/"
cp "$SCRIPT_DIR/../divisions.json" "$scratch/repo/"
export HOME="$scratch/home"
for agent in first second; do
printf '%s\n' '---' "name: $agent" 'description: Example agent' 'color: blue' '---' '# Example agent' > "$scratch/repo/engineering/$agent.md"
done
run_install() {
bash "$scratch/repo/scripts/install.sh" --tool claude-code --division engineering \
--no-convert --link --path "$scratch/dest" > "$scratch/result.log" 2>&1
}
mkdir -p "$scratch/dest"
printf 'user content\n' > "$scratch/dest/first.md"
run_install
[[ ! -L "$scratch/dest/first.md" ]] && grep -qx 'user content' "$scratch/dest/first.md" || {
echo 'FAIL: --link replaced a user-owned regular file' >&2; exit 1;
}
[[ -L "$scratch/dest/second.md" ]]
grep -q 'Not installed: 1 file' "$scratch/result.log"
for kind in foreign dangling directory; do
rm -f "$scratch/dest/first.md" "$scratch/dest/second.md"
target="$scratch/$kind"
[[ "$kind" == foreign ]] && printf 'outside content\n' > "$target"
[[ "$kind" == directory ]] && mkdir -p "$target"
ln -s "$target" "$scratch/dest/first.md"
run_install
[[ "$(readlink "$scratch/dest/first.md")" == "$target" ]]
[[ -L "$scratch/dest/second.md" ]]
grep -q 'Not installed: 1 file' "$scratch/result.log"
[[ "$kind" != foreign ]] || grep -qx 'outside content' "$target"
[[ "$kind" != dangling ]] || [[ ! -e "$target" ]]
[[ "$kind" != directory ]] || [[ ! -e "$target/first.md" ]]
done
rm -f "$scratch/dest/first.md"
ln -s "$scratch/repo/engineering/old.md" "$scratch/dest/first.md"
run_install
[[ "$(readlink "$scratch/dest/first.md")" == "$scratch/repo/engineering/first.md" ]]
run_install
[[ "$(readlink "$scratch/dest/first.md")" == "$scratch/repo/engineering/first.md" ]]
# A config directory itself may be a user's dotfiles link; protect leaf files.
mv "$scratch/dest" "$scratch/dotfiles"
ln -s "$scratch/dotfiles" "$scratch/dest"
run_install
[[ -L "$scratch/dest" && -L "$scratch/dest/second.md" ]]
echo 'PASS: --link skips user files and foreign links, replaces its own links, and continues'
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
scratch="$(mktemp -d)"
trap 'rm -rf "$scratch"' EXIT
mkdir -p "$scratch/repo/scripts" "$scratch/repo/engineering" "$scratch/repo/integrations" "$scratch/home"
cp "$SCRIPT_DIR/install.sh" "$SCRIPT_DIR/lib.sh" "$scratch/repo/scripts/"
cp "$SCRIPT_DIR/../divisions.json" "$scratch/repo/"
export HOME="$scratch/home"
export CLAUDE_CONFIG_DIR="$HOME/.claude" COPILOT_AGENT_DIR="$HOME/.github/agents"
for agent in first second; do
printf '%s\n' '---' "name: $agent" 'description: Example agent' 'color: blue' '---' '# Example agent' > "$scratch/repo/engineering/$agent.md"
done
mkdir -p "$scratch/bin" "$scratch/tmp"
cat > "$scratch/bin/cp" <<'EOF'
#!/usr/bin/env bash
if [[ "$*" == *"/.claude/agents/"* && "${FAIL_WORKER:-yes}" == yes ]]; then
echo 'fixture worker copy failed' >&2
exit 47
fi
exec /bin/cp "$@"
EOF
chmod +x "$scratch/bin/cp"
export PATH="$scratch/bin:$PATH" TMPDIR="$scratch/tmp"
expected_status=0
printf 'failure\n' | xargs -P 2 -I {} sh -c 'exit 47' || expected_status=$?
status=0
bash "$scratch/repo/scripts/install.sh" --tool claude-code,copilot --division engineering \
--no-convert --parallel --jobs 2 > "$scratch/failure.log" 2>&1 || status=$?
[[ "$status" == "$expected_status" && "$status" != 0 ]] || { cat "$scratch/failure.log"; echo "FAIL: worker failure status changed: $status"; exit 1; }
grep -q 'fixture worker copy failed' "$scratch/failure.log" || {
echo 'FAIL: buffered worker failure is hidden' >&2; exit 1;
}
[[ -f "$HOME/.github/agents/first.md" ]]
! grep -q 'Done! Installed' "$scratch/failure.log"
[[ -z "$(ls -A "$scratch/tmp")" ]] || { echo 'FAIL: worker output directory leaked'; exit 1; }
export FAIL_WORKER=no
bash "$scratch/repo/scripts/install.sh" --tool claude-code,copilot --division engineering \
--no-convert --parallel --jobs 2 > "$scratch/success.log" 2>&1
grep -q 'Done! Installed 2 tool(s)' "$scratch/success.log"
[[ -f "$HOME/.claude/agents/first.md" ]]
[[ -z "$(ls -A "$scratch/tmp")" ]]
echo 'PASS: failed parallel worker logs are replayed, status is preserved, and buffers are removed'
+23 -5
View File
@@ -330,9 +330,9 @@ echo ""
echo "parallel workers"
# Two tools that write the same filenames into one shared --path would silently
# overwrite each other (claude-code and copilot both copy the raw source as
# <division>-<slug>.md). The installer must refuse, not clobber. Both tools work
# under --no-convert, which keeps this case cheap in CI.
# overwrite each other (claude-code and copilot both copy the raw source under
# its own name). The installer must refuse, not clobber. Both tools work under
# --no-convert, which keeps this case cheap in CI.
home="$(sandbox path-collision)"
dest="$home/My [Agents]/dest dir"
run_install "$home" --tool claude-code,copilot --no-convert --agent "$FIRST_ENG_SLUG" --path "$dest"
@@ -340,8 +340,22 @@ assert_eq 1 "$RUN_STATUS" "two tools that write the same filenames into one --pa
assert_eq 1 "$(printf '%s' "$RUN_OUT" | grep -c 'overwrite')" "the refusal explains the collision"
assert_eq 0 "$(count_md "$dest")" "a refused install writes nothing"
# A source file's own name is not always <division>-<slug>.md: 73 agents are
# named <slug>.md, which is what the converted .md tools write. claude-code and
# qwen with one of those used to report two successful installs while qwen's
# file replaced Claude Code's. The refusal comes before any conversion, so this
# stays cheap too.
slug_named="$(for f in "$REPO_ROOT"/game-development/*.md; do
is_agent_file "$f" && [[ "$(basename "$f" .md)" == "$(agent_slug "$f")" ]] && { agent_slug "$f"; break; }
done)"
home="$(sandbox path-collision-slug-named)"
dest="$home/dest"
run_install "$home" --tool claude-code,qwen --agent "$slug_named" --path "$dest"
assert_eq 1 "$RUN_STATUS" "claude-code and qwen share <slug>.md names, so one --path is refused ($slug_named)"
assert_eq 0 "$(count_md "$dest")" "the refused claude-code+qwen install writes nothing"
# The propagation cases below therefore use a NON-colliding pair: claude-code
# writes <division>-<slug>.md and codex writes <slug>.toml, so BOTH outputs must
# writes .md and codex writes <slug>.toml, so BOTH outputs must
# survive in the shared --path — which is a stronger check than one tool's count
# alone (a count of 1 cannot tell "two wrote, one clobbered" from "one wrote").
# codex has no committed output (integrations/ is generated and gitignored), so
@@ -361,7 +375,11 @@ dest="$home/My [Agents]/dest dir"
list="$home/my agents list.txt"
{ echo "# one agent, listed in a file whose own path has spaces"; echo "$FIRST_ENG_SLUG"; } > "$list"
run_install "$home" --tool claude-code,codex --parallel --jobs 1 --agents-file "$list" --path "$dest"
assert_eq 0 "$RUN_STATUS" "--parallel with a spaced/globbed --path exits 0"
# This used to pass for the wrong reason: the workers get the spaced path split
# into words, reject the stray words as unknown options, and usage() exited 0,
# so every worker "succeeded" having installed nothing (the count below).
# Unknown options exit 1 now, so the exit code tells the truth until #755 lands.
xfail_eq 0 "$RUN_STATUS" "--parallel with a spaced/globbed --path exits 0" "PR #755"
xfail_eq 1 "$(count_md "$dest")" \
"--parallel installs exactly the one selected agent (spaced --path + --agents-file)" "PR #755"
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env bash
# Code fences that GitHub renders differently from what the author meant must
# fail lint, and the fix the error message suggests must pass it.
#
# Markdown has no nesting at equal fence length. Inside a ```markdown template,
# a ```bash line is text, and the example's closing ``` ends the template, so
# the rest of it renders as headings and prose. Seven roster agents shipped
# that way, three of them with the last block left open to the end of the file.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FIXTURE="$(mktemp -d "${TMPDIR:-/tmp}/agency-lint-fences.XXXXXX")"
trap 'rm -rf "$FIXTURE"' EXIT
fail() { echo "FAIL: $*" >&2; exit 1; }
frontmatter() {
cat <<'EOF'
---
name: Fence Fixture
description: Fixture agent for the code-fence lint rules
color: blue
---
## Identity
Enough words to clear the short-body warning, repeated so the linter has a
body to read: this fixture exists to check how fenced code blocks are parsed.
## Core Mission
Show a template that contains a code example.
## Critical Rules
Keep fences balanced.
EOF
}
# 1. ```bash inside ```markdown: the inner fence does not nest.
{ frontmatter; cat <<'EOF'
### Template
```markdown
# Report
```bash
npm test
```
## Findings
```
EOF
} > "$FIXTURE/nested.md"
if bash "$SCRIPT_DIR/lint-agents.sh" "$FIXTURE/nested.md" > "$FIXTURE/nested.log" 2>&1; then
fail "linter accepted a \`\`\`bash fence nested in a \`\`\`markdown block of the same length"
fi
grep -Fq "nested.md:16: '\`\`\`bash' inside the block opened at line 14 does not nest" "$FIXTURE/nested.log" \
|| { cat "$FIXTURE/nested.log" >&2; fail "nested-fence error is missing or names the wrong lines"; }
# 2. A block left open to the end of the file.
{ frontmatter; printf '%s\n' '### Layout' '```' 'css/' 'js/'; } > "$FIXTURE/unclosed.md"
if bash "$SCRIPT_DIR/lint-agents.sh" "$FIXTURE/unclosed.md" > "$FIXTURE/unclosed.log" 2>&1; then
fail "linter accepted a code block that is never closed"
fi
grep -Fq "unclosed.md:14: code block is never closed" "$FIXTURE/unclosed.log" \
|| { cat "$FIXTURE/unclosed.log" >&2; fail "unclosed-fence error is missing or names the wrong line"; }
# 3. The fix the message suggests — a longer outer fence — passes, and so do
# tilde fences and a shorter run inside a longer block.
{ frontmatter; cat <<'EOF'
### Template
````markdown
# Report
```bash
npm test
```
## Findings
````
~~~text
```python is just text in here
~~~
EOF
} > "$FIXTURE/valid.md"
bash "$SCRIPT_DIR/lint-agents.sh" "$FIXTURE/valid.md" > "$FIXTURE/valid.log" 2>&1 \
|| { cat "$FIXTURE/valid.log" >&2; fail "linter rejected correctly nested fences"; }
# 4. The helper itself, which the OpenClaw split in convert.sh shares: only a
# bare run of the same character, at least as long, closes a block.
. "$SCRIPT_DIR/lib.sh"
fence_closes_p '```python' '`' 3 0 && fail "fence_closes_p treated '\`\`\`python' as a closing fence"
fence_closes_p '```' '`' 3 0 || fail "fence_closes_p rejected a bare closing fence"
fence_closes_p '```` ' '`' 3 0 || fail "fence_closes_p rejected a longer closing fence with trailing spaces"
fence_closes_p '```' '`' 4 0 && fail "fence_closes_p let a shorter run close a longer fence"
echo "PASS: nested and unclosed fences are rejected; correctly nested fences pass"
+75 -34
View File
@@ -233,43 +233,70 @@ class DependencyScanner:
"CVE-2023-XXXXX": "Not exploitable in our configuration — validated by AppSec team 2024-01-15",
}
def scan_npm(self, project_path: Path) -> list[VulnFinding]:
"""Scan Node.js dependencies using npm audit."""
@staticmethod
def audit_json(command: list[str], project_path: Path) -> dict:
"""Exit 1 may mean findings; tool errors are never a clean scan."""
result = subprocess.run(
["npm", "audit", "--json", "--production"],
cwd=project_path, capture_output=True, text=True
command, cwd=project_path, capture_output=True, text=True
)
findings = []
if result.stdout:
try:
audit = json.loads(result.stdout)
for vuln_id, vuln in audit.get("vulnerabilities", {}).items():
findings.append(VulnFinding(
package=vuln_id,
version=vuln.get("range", "unknown"),
severity=Severity(vuln.get("severity", "low")),
cve=vuln.get("via", [{}])[0].get("url", "N/A") if vuln.get("via") else "N/A",
fixed_version=vuln.get("fixAvailable", {}).get("version", "N/A")
if isinstance(vuln.get("fixAvailable"), dict) else "N/A",
description=vuln.get("via", [{}])[0].get("title", "")
if isinstance(vuln.get("via", [None])[0], dict) else str(vuln.get("via", "")),
))
except json.JSONDecodeError as exc:
raise RuntimeError(f"{command[0]} did not produce valid JSON") from exc
if result.returncode not in (0, 1) or not isinstance(audit, dict) or audit.get("error"):
raise RuntimeError(f"{command[0]} failed (exit {result.returncode})")
return audit
def scan_npm(self, project_path: Path) -> list[VulnFinding]:
"""Scan Node.js dependencies using npm audit's current JSON report."""
audit = self.audit_json(["npm", "audit", "--json", "--omit=dev"], project_path)
vulnerabilities = audit.get("vulnerabilities")
if not isinstance(vulnerabilities, dict):
raise RuntimeError("npm audit report is missing vulnerabilities")
findings = []
for package, vuln in vulnerabilities.items():
# via contains advisory objects OR names of indirect dependencies.
via = vuln.get("via", [])
advisories = [item for item in via if isinstance(item, dict)]
fix = vuln.get("fixAvailable")
severity = vuln.get("severity")
if severity not in {item.value for item in Severity} | {"info"}:
raise RuntimeError(f"npm audit returned unknown severity for {package}")
findings.append(VulnFinding(
package=package,
version=vuln.get("range", "unknown"),
severity=Severity.LOW if severity == "info" else Severity(severity),
cve=advisories[0].get("url", "N/A") if advisories else "N/A",
fixed_version=(fix.get("version", "N/A") if isinstance(fix, dict)
else "available" if fix is True else "N/A"),
description="; ".join(item.get("title", "") for item in advisories)
or "Indirect dependency vulnerability: " + ", ".join(map(str, via)),
))
return findings
def scan_python(self, project_path: Path) -> list[VulnFinding]:
"""Scan Python dependencies using pip-audit."""
result = subprocess.run(
["pip-audit", "--format=json", "--desc"],
cwd=project_path, capture_output=True, text=True
)
"""Audit requirements, or the active environment with the project installed."""
command = ["pip-audit", "--format=json", "--desc"]
if (project_path / "requirements.txt").exists():
command.extend(["-r", "requirements.txt"])
audit = self.audit_json(command, project_path)
dependencies = audit.get("dependencies")
if not isinstance(dependencies, list):
raise RuntimeError("pip-audit report is missing dependencies")
findings = []
if result.stdout:
for vuln in json.loads(result.stdout):
for dependency in dependencies:
if dependency.get("skip_reason"):
raise RuntimeError(f"pip-audit skipped {dependency['name']}")
vulnerabilities = dependency.get("vulns")
if not isinstance(vulnerabilities, list):
raise RuntimeError("pip-audit dependency is missing vulns")
for vuln in vulnerabilities:
findings.append(VulnFinding(
package=vuln["name"],
version=vuln["version"],
severity=Severity.HIGH, # pip-audit doesn't always provide severity
cve=vuln.get("id", "N/A"),
fixed_version=vuln.get("fix_versions", ["N/A"])[0],
package=dependency["name"],
version=dependency["version"],
severity=Severity.HIGH, # Conservative local policy, not tool-provided severity
cve=vuln["id"],
fixed_version=", ".join(vuln.get("fix_versions", [])) or "N/A",
description=vuln.get("description", ""),
))
return findings
@@ -307,12 +334,17 @@ def main():
scanner = DependencyScanner()
project = Path(".")
# Detect project type and scan
# Detect project type and scan. An unavailable scanner, malformed report,
# unsupported report shape, or skipped dependency leaves coverage incomplete.
findings = []
if (project / "package.json").exists():
findings.extend(scanner.scan_npm(project))
if (project / "requirements.txt").exists() or (project / "pyproject.toml").exists():
findings.extend(scanner.scan_python(project))
try:
if (project / "package.json").exists():
findings.extend(scanner.scan_npm(project))
if (project / "requirements.txt").exists() or (project / "pyproject.toml").exists():
findings.extend(scanner.scan_python(project))
except (OSError, RuntimeError, KeyError, TypeError, ValueError) as exc:
print(f"SCAN INCOMPLETE: {exc}", file=sys.stderr)
sys.exit(2)
# Enforce policy
passed, violations = scanner.enforce_policy(findings)
@@ -331,6 +363,15 @@ if __name__ == "__main__":
main()
```
For a `pyproject.toml` project, install its locked runtime dependencies into an
isolated environment before invoking this wrapper; `pip-audit` without `-r`
audits the active environment. Exit 2 means the gate could not complete and must
block promotion until the scanner/report problem is resolved. Exercise fixtures
for clean reports, advisory findings, indirect npm `via` strings, empty Python
`fix_versions`, scanner failures, invalid JSON, and skipped dependencies. See the
[pip-audit JSON format and exit codes](https://github.com/pypa/pip-audit#usage)
and [npm audit report behavior](https://docs.npmjs.com/cli/v11/commands/npm-audit).
### Threat Model Template (STRIDE)
```markdown
# Threat Model: [Feature/System Name]
@@ -150,6 +150,12 @@ contract SecureLending {
AggregatorV3Interface immutable priceFeed;
uint256 constant MAX_ORACLE_STALENESS = 1 hours;
constructor(address feed) {
priceFeed = AggregatorV3Interface(feed);
}
// amount uses the collateral token's base units. With a USD/token feed,
// the result is USD scaled by the collateral token's decimal count.
function getCollateralValue(uint256 amount) public view returns (uint256) {
(
uint80 roundId,
@@ -164,11 +170,24 @@ contract SecureLending {
require(updatedAt > block.timestamp - MAX_ORACLE_STALENESS, "Stale price");
require(answeredInRound >= roundId, "Incomplete round");
return (amount * uint256(price)) / priceFeed.decimals();
uint8 feedDecimals = priceFeed.decimals();
require(feedDecimals <= 77, "Unsupported feed decimals");
// decimals() is the number of decimal places, not the scale factor.
return (amount * uint256(price)) / (10 ** uint256(feedDecimals));
}
}
```
Before comparing collateral value with debt, normalize both to the same unit.
For one 18-decimal token (`amount = 1e18`) priced at $2,000 by an 8-decimal
feed (`price = 2000e8`), this function returns `2000e18`, not `25000000000e18`.
A 6-decimal token produces `2000e6`; converting that to a debt asset's base
units is a separate step. Test both scales and a zero-decimal feed, which must
not divide by zero. Solidity's checked multiplication still reverts on extreme
products; production code should use a reviewed full-precision `mulDiv` if its
supported input range can overflow. Verify the feed's quote asset and decimal
count using the [Chainlink API reference](https://docs.chain.link/data-feeds/api-reference).
### Access Control Audit Checklist
```markdown
# Access Control Audit Checklist
@@ -297,6 +297,50 @@ spec:
- protocol: TCP
port: 5432
---
# Sender egress must also allow frontend → backend API under default-deny
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-api-egress
namespace: production
spec:
podSelector:
matchLabels:
app: frontend
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: backend-api
ports:
- protocol: TCP
port: 8080
---
# Sender egress must also allow backend API → database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api-database-egress
namespace: production
spec:
podSelector:
matchLabels:
app: backend-api
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
---
# Allow DNS egress for all pods (required for service discovery)
apiVersion: networking.k8s.io/v1
@@ -323,6 +367,15 @@ spec:
port: 53
```
Both the sender's egress and the receiver's ingress must permit a connection.
The selectors above target pods in `production`; they do not grant the same
labels in other namespaces access. Use a NetworkPolicy-enforcing CNI and verify
frontend → API:8080 and API → database:5432 succeed, while frontend → database,
API → database:5433, and API → arbitrary external destinations remain blocked.
DNS labels must match the cluster's actual DNS pods; NodeLocal DNS needs a
cluster-specific policy. Reply traffic for an allowed connection is implicit.
See [Kubernetes NetworkPolicy semantics](https://kubernetes.io/docs/concepts/services-networking/network-policies/).
### CI/CD Pipeline Security (GitHub Actions with OIDC)
```yaml
# Secure deployment pipeline — no long-lived credentials
+15 -2
View File
@@ -206,14 +206,27 @@ Write-Host "[!] NEXT: Copy $outDir to analysis workstation — do NOT analyze on
```
### Linux Forensic Triage Script
A suspected host may already be hostile: use trusted collection tools and an
approved evidence destination. This local triage is not a substitute for a
forensic image. Restrict the collection directory to the collector, preserve it
for handoff, and never write through a pre-existing path supplied by another user.
```bash
#!/bin/bash
# Linux Incident Response Triage Collection
# Run as root on suspected compromised system
TIMESTAMP=$(date -u +"%Y%m%d-%H%M%S")
OUTDIR="/tmp/ir-triage-${HOSTNAME}-${TIMESTAMP}"
mkdir -p "$OUTDIR"
# Evidence can contain credentials. Create a private directory atomically;
# never reuse a predictable path in /tmp or trust an inherited TMPDIR.
umask 077
OUTDIR=$(mktemp -d /tmp/ir-triage.XXXXXXXXXX) || {
echo "[!] Unable to create private evidence directory" >&2
exit 1
}
readonly OUTDIR
# Preserve the directory for handoff; do not delete evidence in an EXIT trap.
echo "[*] Starting Linux IR triage at ${TIMESTAMP} UTC"
@@ -87,7 +87,7 @@ jobs:
### Static Key → Dynamic, Short-Lived Credential
```hcl
```bash
# BEFORE: a long-lived static DB password in an env var — one leak = full, permanent access.
# DATABASE_URL=postgres://app:sup3rs3cret@db.internal:5432/app # never rotated, everywhere
@@ -95,11 +95,22 @@ jobs:
vault write database/roles/app \
db_name=appdb \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
GRANT SELECT, INSERT, UPDATE ON app.* TO \"{{name}}\";" \
GRANT USAGE ON SCHEMA app TO \"{{name}}\"; \
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA app TO \"{{name}}\";" \
default_ttl="15m" max_ttl="1h"
# The app fetches a fresh, least-privilege credential per session; a leaked one is dead in minutes.
```
This PostgreSQL example assumes the dedicated `app` schema contains only tables
this workload may access, and the Vault database connection role can create roles
and grant those privileges. `app.*` is not PostgreSQL GRANT syntax; schema `USAGE`
and table privileges are separate. The grants cover existing tables only. Reissue
credentials after migrations or maintain a reviewed grant strategy for future
tables. Sequence-backed inserts need narrowly scoped sequence `USAGE` as well.
Verify a leased role can SELECT/INSERT/UPDATE an allowed table but cannot DELETE,
CREATE a table, or access another schema. See [PostgreSQL GRANT](https://www.postgresql.org/docs/current/sql-grant.html)
and [Vault's database secrets tutorial](https://developer.hashicorp.com/vault/tutorials/db-credentials/database-secrets).
### Leak-Response Runbook (the clock started at commit)
```markdown
+18 -5
View File
@@ -122,7 +122,6 @@ fields:
### Compiled to Splunk SPL
```spl
| Suspicious PowerShell Encoded Command — compiled from Sigma rule
index=windows sourcetype=WinEventLog:Sysmon EventCode=1
(ParentImage="*\\cmd.exe" OR ParentImage="*\\wscript.exe"
OR ParentImage="*\\cscript.exe" OR ParentImage="*\\mshta.exe"
@@ -135,7 +134,6 @@ index=windows sourcetype=WinEventLog:Sysmon EventCode=1
ParentImage LIKE "%mshta.exe", 85,
1=1, 70
)
| where NOT match(CommandLine, "(?i)(SCCM|ConfigMgr|Intune)")
| table _time Computer User ParentImage Image CommandLine risk_score
| sort - risk_score
```
@@ -152,9 +150,6 @@ DeviceProcessEvents
| where ProcessCommandLine has_any (
"-enc ", "-EncodedCommand", "-ec ", "FromBase64String"
)
// Exclude known legitimate automation
| where ProcessCommandLine !contains "SCCM"
and ProcessCommandLine !contains "ConfigMgr"
| extend RiskScore = case(
InitiatingProcessFileName =~ "wmiprvse.exe", 90,
InitiatingProcessFileName =~ "mshta.exe", 85,
@@ -165,6 +160,24 @@ DeviceProcessEvents
| sort by RiskScore desc
```
### Validate Exceptions Against Attacker-Controlled Input
Keep these example queries free of command-line substring exclusions. An attacker
can append `# SCCM`, `# ConfigMgr`, or `# Intune` to a suspicious PowerShell command;
that string does not establish that a trusted deployment system launched it.
Investigate the alert using host enrollment, expected service identity, verified
parent binary path/signature, and the deployment job's audit trail. If an exception
is approved, scope it to that evidence, record an owner and expiry, and test it
against benign automation and malicious lookalikes. A parent executable name alone
is not sufficient either. Keep the Sigma and SIEM implementations equivalent and
label any environment-specific exception explicitly.
Replay the same positive process event with all four command lines: the original,
then the original plus each of those three comments. All four must alert. Include
a negative non-PowerShell event to prove the rule is not matching everything.
Use [Sigma rule testing and tuning guidance](https://sigmahq.io/docs/basics/rules.html)
and the target SIEM's own query test facilities before deployment.
### MITRE ATT&CK Coverage Assessment Template
```markdown
# MITRE ATT&CK Detection Coverage Report
@@ -95,7 +95,7 @@ You are a **Developer Advocate**, the trusted engineer who lives at the intersec
```
### Viral Tutorial Structure
```markdown
````markdown
# Build a [Real Thing] with [Your Platform] in [Honest Time]
**Live demo**: [link] | **Full source**: [GitHub link]
@@ -144,7 +144,7 @@ Ready to go further?
- → [Add authentication to your dashboard](link)
- → [Deploy to production on Vercel](link)
- → [Explore the full API reference](link)
```
````
### Conference Talk Proposal Template
```markdown
@@ -179,7 +179,7 @@ Why this speaker: relevant experience and credibility signal.]
```
### GitHub Issue Response Templates
```markdown
````markdown
<!-- For bug reports with reproduction steps -->
Thanks for the detailed report and reproduction case — that makes debugging much faster.
@@ -206,7 +206,7 @@ likelihood/priority].
In the meantime, here's how some community members work around this today: [link or snippet].
```
````
### Developer Survey Design
```javascript
@@ -62,7 +62,7 @@ You operate under a strict **Zero Code Execution** guardrail: you design the blu
### The 5-Part Standard Implementation Plan Schema (`.md`)
```markdown
````markdown
# 🏛️ [Project/Module Name] — Architectural Blueprint & Governance Plan
## 1. 🎓 Conceptual Masterclass: Philosophy, First Principles & Landscape
@@ -99,7 +99,7 @@ graph TD
## 5. 🔄 Rollback Strategy & Failure Containment
- **Instant Rollback Path:** Steps to revert changes in under 60 seconds without data loss.
- **Circuit Breakers:** Degradation mode if downstream dependencies fail.
```
````
---
+13 -1
View File
@@ -110,6 +110,12 @@ await server.connect(transport);
### Python MCP Server
```python
# MCP Python SDK 1.x: pip install 'mcp>=1,<2' httpx
# SDK 2.x uses a different server API; this example targets FastMCP.
import json
import os
from pathlib import Path
import httpx
from mcp.server.fastmcp import FastMCP
from pydantic import Field
@@ -133,7 +139,13 @@ async def search_issues(
headers={"Authorization": f"token {os.environ['GITHUB_TOKEN']}"},
)
resp.raise_for_status()
issues = [{"number": i["number"], "title": i["title"], "author": i["user"]["login"], "labels": [l["name"] for l in i["labels"]]} for i in resp.json()]
# GitHub's repository issues endpoint also returns pull requests.
# Keep this tool's issue-only contract; limit bounds the listing page.
issues = [
{"number": i["number"], "title": i["title"],
"author": i["user"]["login"], "labels": [l["name"] for l in i["labels"]]}
for i in resp.json() if "pull_request" not in i
]
return json.dumps(issues, indent=2)
@mcp.resource("repo://readme")
+27 -18
View File
@@ -109,26 +109,35 @@ import numpy as np
import pandas as pd
def compute_psi(expected: pd.Series, actual: pd.Series, bins: int = 10) -> float:
"""Baseline-quantile PSI; include out-of-range observations in tail bins.
<0.10: little shift; 0.10–0.25: investigate; >=0.25: significant shift.
These are monitoring heuristics, not a model validity certificate.
"""
Compute Population Stability Index between two distributions.
Interpretation:
< 0.10 → No significant shift (green)
0.10–0.25 → Moderate shift, investigation recommended (amber)
>= 0.25 → Significant shift, action required (red)
"""
breakpoints = np.linspace(0, 100, bins + 1)
expected_pcts = np.percentile(expected.dropna(), breakpoints)
if not isinstance(bins, int) or isinstance(bins, bool) or bins < 2:
raise ValueError("bins must be an integer >= 2")
baseline = expected.dropna().to_numpy(dtype=float)
observed = actual.dropna().to_numpy(dtype=float)
if not len(baseline) or not len(observed):
raise ValueError("PSI requires nonempty baseline and observed samples")
if not np.isfinite(baseline).all() or not np.isfinite(observed).all():
raise ValueError("PSI samples must be finite")
expected_counts = np.histogram(expected, bins=expected_pcts)[0]
actual_counts = np.histogram(actual, bins=expected_pcts)[0]
# Laplace smoothing to avoid division by zero
exp_pct = (expected_counts + 1) / (expected_counts.sum() + bins)
act_pct = (actual_counts + 1) / (actual_counts.sum() + bins)
psi = np.sum((act_pct - exp_pct) * np.log(act_pct / exp_pct))
return round(psi, 6)
# Unique interior quantiles handle repeated/constant baseline values.
interior = np.unique(np.percentile(baseline, np.linspace(0, 100, bins + 1)[1:-1]))
if np.all(baseline == baseline[0]):
# A point-mass baseline needs its own equality bucket. Otherwise a
# move entirely ABOVE that value shares the same open-ended tail.
value = baseline[0]
interior = np.array([value, np.nextafter(value, np.inf)])
edges = np.unique(np.concatenate(([-np.inf], interior, [np.inf])))
expected_counts = np.histogram(baseline, bins=edges)[0]
actual_counts = np.histogram(observed, bins=edges)[0]
bucket_count = len(edges) - 1
# Normalize smoothing with the actual number of nonduplicate buckets.
exp_pct = (expected_counts + 1) / (len(baseline) + bucket_count)
act_pct = (actual_counts + 1) / (len(observed) + bucket_count)
return round(float(np.sum((act_pct - exp_pct) * np.log(act_pct / exp_pct))), 6)
```
### Discrimination Metrics (Gini & KS)
@@ -229,7 +229,7 @@ Every time I make an assumption that I cannot verify from the available code and
Every workflow spec follows this structure:
```markdown
````markdown
# WORKFLOW: [Name]
**Version**: 0.1
**Date**: YYYY-MM-DD
@@ -392,7 +392,7 @@ Every workflow spec follows this structure:
| Date | Finding | Action taken |
|---|---|---|
| YYYY-MM-DD | Initial spec created | — |
```
````
### Discovery Audit Checklist
+14 -3
View File
@@ -93,7 +93,12 @@ You are **AccessibilityAuditor**, an expert accessibility specialist who ensures
- Moderate: [Count] — Causes difficulty but has workarounds
- Minor: [Count] — Annoyances that reduce usability
**WCAG Conformance**: DOES NOT CONFORM / PARTIALLY CONFORMS / CONFORMS
**Audit Scope**: [URLs, complete processes, UI states, date, and technologies tested]
**Criteria Results**: [PASS / FAIL / NOT TESTED / NOT APPLICABLE, with evidence]
**WCAG Conformance**: [DOES NOT CONFORM if any in-scope A/AA criterion fails;
NOT DETERMINED if required tests are incomplete; CONFORMS only after evaluating
all applicable A/AA criteria for full pages and complete processes]
**Untested Scope**: [Pages, states, or assistive technology combinations not assessed]
**Assistive Technology Compatibility**: FAIL / PARTIAL / PASS
## 🚨 Issues Found
@@ -218,8 +223,9 @@ You are **AccessibilityAuditor**, an expert accessibility specialist who ensures
### Step 1: Automated Baseline Scan
```bash
# Run axe-core against all pages
npx @axe-core/cli http://localhost:8000 --tags wcag2a,wcag2aa,wcag22aa
# Automated subset of WCAG 2.2 A/AA: include criteria introduced in 2.1.
# Scan each in-scope URL and relevant UI state; one URL is not the whole site.
npx @axe-core/cli http://localhost:8000 --tags wcag2a,wcag2aa,wcag21a,wcag21aa,wcag22aa
# Run Lighthouse accessibility audit
npx lighthouse http://localhost:8000 --only-categories=accessibility --output=json
@@ -229,6 +235,11 @@ npx lighthouse http://localhost:8000 --only-categories=accessibility --output=js
# Identify all custom interactive components for manual testing
```
Automated results cover only the rules the tool can evaluate, even with every
WCAG tag selected. Use the [axe-core tag inventory](https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#axe-core-tags)
and the [WCAG conformance requirements](https://www.w3.org/TR/WCAG22/#conformance-reqs)
to document scope; a clean scan or a sampled page does not establish site conformance.
### Step 2: Manual Assistive Technology Testing
- Navigate every user journey with keyboard only — no mouse
- Complete all critical flows with a screen reader (VoiceOver on macOS, NVDA on Windows)
+34 -22
View File
@@ -58,16 +58,20 @@ You are **API Tester**, an expert API testing specialist who focuses on comprehe
## 📋 Your Technical Deliverables
### Comprehensive API Test Suite Example
```javascript
```typescript
// Save as tests/api.spec.ts. Run only against an authorized test environment.
// Advanced API test automation with security and performance
import { test, expect } from '@playwright/test';
import { performance } from 'perf_hooks';
describe('User API Comprehensive Testing', () => {
test.describe('User API Comprehensive Testing', () => {
let authToken: string;
let baseURL = process.env.API_BASE_URL;
const baseURL = process.env.API_BASE_URL;
if (!baseURL || !process.env.TEST_USER_PASSWORD) {
throw new Error('API_BASE_URL and TEST_USER_PASSWORD are required');
}
beforeAll(async () => {
test.beforeAll(async () => {
// Authenticate and get token
const response = await fetch(`${baseURL}/auth/login`, {
method: 'POST',
@@ -77,11 +81,14 @@ describe('User API Comprehensive Testing', () => {
password: process.env.TEST_USER_PASSWORD
})
});
expect(response.status).toBe(200);
const data = await response.json();
expect(typeof data.token).toBe('string');
expect(data.token.length).toBeGreaterThan(0);
authToken = data.token;
});
describe('Functional Testing', () => {
test.describe('Functional Testing', () => {
test('should create user with valid data', async () => {
const userData = {
name: 'Test User',
@@ -127,7 +134,7 @@ describe('User API Comprehensive Testing', () => {
});
});
describe('Security Testing', () => {
test.describe('Security Testing', () => {
test('should reject requests without authentication', async () => {
const response = await fetch(`${baseURL}/users`, {
method: 'GET'
@@ -137,7 +144,7 @@ describe('User API Comprehensive Testing', () => {
test('should prevent SQL injection attempts', async () => {
const sqlInjection = "'; DROP TABLE users; --";
const response = await fetch(`${baseURL}/users?search=${sqlInjection}`, {
const response = await fetch(`${baseURL}/users?search=${encodeURIComponent(sqlInjection)}`, {
headers: { 'Authorization': `Bearer ${authToken}` }
});
expect(response.status).not.toBe(500);
@@ -145,9 +152,12 @@ describe('User API Comprehensive Testing', () => {
});
test('should enforce rate limiting', async () => {
// Separate account/token: exhausting its quota must not poison other tests.
const rateLimitToken = process.env.RATE_LIMIT_TEST_TOKEN;
if (!rateLimitToken) throw new Error('RATE_LIMIT_TEST_TOKEN is required');
const requests = Array(100).fill(null).map(() =>
fetch(`${baseURL}/users`, {
headers: { 'Authorization': `Bearer ${authToken}` }
headers: { 'Authorization': `Bearer ${rateLimitToken}` }
})
);
@@ -157,7 +167,7 @@ describe('User API Comprehensive Testing', () => {
});
});
describe('Performance Testing', () => {
test.describe('Performance Testing', () => {
test('should respond within performance SLA', async () => {
const startTime = performance.now();
@@ -165,6 +175,7 @@ describe('User API Comprehensive Testing', () => {
headers: { 'Authorization': `Bearer ${authToken}` }
});
await response.arrayBuffer(); // Include response body transfer in latency
const endTime = performance.now();
const responseTime = endTime - startTime;
@@ -174,26 +185,27 @@ describe('User API Comprehensive Testing', () => {
test('should handle concurrent requests efficiently', async () => {
const concurrentRequests = 50;
const requests = Array(concurrentRequests).fill(null).map(() =>
fetch(`${baseURL}/users`, {
const samples = await Promise.all(Array.from({ length: concurrentRequests }, async () => {
const start = performance.now();
const response = await fetch(`${baseURL}/users`, {
headers: { 'Authorization': `Bearer ${authToken}` }
})
);
});
await response.arrayBuffer();
return { status: response.status, durationMs: performance.now() - start };
}));
const startTime = performance.now();
const responses = await Promise.all(requests);
const endTime = performance.now();
const allSuccessful = responses.every(r => r.status === 200);
const avgResponseTime = (endTime - startTime) / concurrentRequests;
expect(allSuccessful).toBe(true);
expect(avgResponseTime).toBeLessThan(500);
expect(samples.every(sample => sample.status === 200)).toBe(true);
const averageLatency = samples.reduce((sum, sample) => sum + sample.durationMs, 0)
/ samples.length;
expect(averageLatency).toBeLessThan(500);
// Batch duration / concurrency measures throughput, not per-request latency.
});
});
});
```
This example assumes the application's documented response schemas, a dedicated test account, a separate `RATE_LIMIT_TEST_TOKEN` account, and an isolated environment whose rate limit is reached within 100 requests. Adapt those contracts before execution. These timing assertions are smoke checks; use repeated load-test samples to substantiate p95 SLAs. A non-500 injection response alone does not establish SQL injection safety.
## 🔄 Your Workflow Process
### Step 1: API Discovery and Analysis
+17 -17
View File
@@ -1,6 +1,6 @@
---
name: Evidence Collector
description: Screenshot-obsessed, fantasy-allergic QA specialist - Default to finding 3-5 issues, requires visual proof for everything
description: Screenshot-obsessed, fantasy-allergic QA specialist - Reports reproducible issues with evidence and marks untested scope honestly
color: orange
emoji: 📸
vibe: Screenshot-obsessed QA who won't approve anything without visual proof.
@@ -19,19 +19,19 @@ You are **EvidenceQA**, a skeptical QA specialist who requires visual proof for
## 🔍 Your Core Beliefs
### "Screenshots Don't Lie"
- Visual evidence is the only truth that matters
- If you can't see it working in a screenshot, it doesn't work
- Screenshots establish visual state; pair them with assertions, traces, or recorded outcomes to establish behavior
- A screenshot of a filled form does not prove submission or persistence
- Claims without evidence are fantasy
- Your job is to catch what others miss
### "Default to Finding Issues"
- First implementations ALWAYS have 3-5+ issues minimum
- "Zero issues found" is a red flag - look harder
- Perfect scores (A+, 98/100) are fantasy on first attempts
- Look actively for defects, but report only reproducible deviations from agreed requirements
- Zero reproducible issues is a valid finding for the tested scope; list remaining coverage gaps
- Never invent issues or downgrade a result to meet a quota or an expected rating
- Be honest about quality levels: Basic/Good/Excellent
### "Prove Everything"
- Every claim needs screenshot evidence
- Every claim needs evidence suited to it: screenshots for appearance, assertions or recorded outcomes for behavior
- Compare what's built vs. what was specified
- Don't add luxury requirements that weren't in the original spec
- Document exactly what you see, not what you think should be there
@@ -100,13 +100,13 @@ echo "COMPREHENSIVE DATA: Device compatibility, dark mode, interactions, full-pa
## 🚫 Your "AUTOMATIC FAIL" Triggers
### Fantasy Reporting Signs
- Any agent claiming "zero issues found"
- Perfect scores (A+, 98/100) on first implementation
- Claims of zero issues without documented test scope and results
- Quality scores without a defined rubric and supporting evidence
- "Luxury/premium" claims without visual evidence
- "Production ready" without comprehensive testing evidence
### Visual Evidence Failures
- Can't provide screenshots
- Missing evidence for a claimed result; record unavailable tests as NOT TESTED rather than a product defect
- Screenshots don't match claims made
- Broken functionality visible in screenshots
- Basic styling claimed as "luxury"
@@ -141,11 +141,11 @@ echo "COMPREHENSIVE DATA: Device compatibility, dark mode, interactions, full-pa
## 🧪 Interactive Testing Results
**Accordion Testing**: [Evidence from before/after screenshots]
**Form Testing**: [Evidence from form interaction screenshots]
**Form Testing**: [Screenshots plus submission response and persisted outcome assertions]
**Navigation Testing**: [Evidence from scroll/click screenshots]
**Mobile Testing**: [Evidence from responsive screenshots]
## 📊 Issues Found (Minimum 3-5 for realistic assessment)
## 📊 Reproducible Issues Found (Zero Is Valid)
1. **Issue**: [Specific problem visible in evidence]
**Evidence**: [Reference to screenshot]
**Priority**: Critical/Medium/Low
@@ -157,15 +157,15 @@ echo "COMPREHENSIVE DATA: Device compatibility, dark mode, interactions, full-pa
[Continue for all issues...]
## 🎯 Honest Quality Assessment
**Realistic Rating**: C+ / B- / B / B+ (NO A+ fantasies)
**Quality Rating**: [Optional agreed rubric and evidence; omit if no rubric exists]
**Design Level**: Basic / Good / Excellent (be brutally honest)
**Production Readiness**: FAILED / NEEDS WORK / READY (default to FAILED)
**Production Readiness**: FAILED / NOT DETERMINED / READY [Against agreed release criteria]
## 🔄 Required Next Steps
**Status**: FAILED (default unless overwhelming evidence otherwise)
**Status**: [FAILED for verified blocking defects; NOT DETERMINED for missing required evidence; READY when agreed gates pass]
**Issues to Fix**: [List specific actionable improvements]
**Timeline**: [Realistic estimate for fixes]
**Re-test Required**: YES (after developer implements fixes)
**Re-test Required**: [YES when fixes or missing tests need verification; otherwise NO]
---
**QA Agent**: EvidenceQA
@@ -189,7 +189,7 @@ Remember patterns like:
- **Which issues get fixed vs. ignored** (track developer response patterns)
### Build Expertise In:
- Spotting broken interactive elements in screenshots
- Pairing screenshots with assertions to establish broken interactive behavior
- Identifying when basic styling is claimed as premium
- Recognizing mobile responsiveness issues
- Detecting when specifications aren't fully implemented
+19 -9
View File
@@ -81,6 +81,8 @@ export const options = {
http_req_duration: ['p(95)<500'], // 95% under 500ms
http_req_failed: ['rate<0.01'], // Error rate under 1%
'response_time': ['p(95)<200'], // Custom metric threshold
checks: ['rate==1'], // Business checks must fail CI, even for HTTP 200
errors: ['rate<0.01'], // Gate the custom application-error metric too
},
};
@@ -88,35 +90,41 @@ export default function () {
const baseUrl = __ENV.BASE_URL || 'http://localhost:3000';
// Test critical user journey
const loginResponse = http.post(`${baseUrl}/api/auth/login`, {
const loginResponse = http.post(`${baseUrl}/api/auth/login`, JSON.stringify({
email: 'test@example.com',
password: __ENV.TEST_USER_PASSWORD
});
}), { headers: { 'Content-Type': 'application/json' } });
// A successful HTTP status can still carry invalid JSON or no token.
let token;
try { token = loginResponse.json('token'); } catch (_) { /* checked below */ }
check(loginResponse, {
const loginOK = check(loginResponse, {
'login successful': (r) => r.status === 200,
'login token present': () => typeof token === 'string' && token.length > 0,
'login response time OK': (r) => r.timings.duration < 200,
});
errorRate.add(loginResponse.status !== 200);
errorRate.add(!loginOK);
responseTimeTrend.add(loginResponse.timings.duration);
throughputCounter.add(1);
if (loginResponse.status === 200) {
const token = loginResponse.json('token');
if (loginOK) {
// Test authenticated API performance
const apiResponse = http.get(`${baseUrl}/api/dashboard`, {
headers: { Authorization: `Bearer ${token}` },
});
check(apiResponse, {
let data;
try { data = apiResponse.json('data'); } catch (_) { /* checked below */ }
const dashboardOK = check(apiResponse, {
'dashboard load successful': (r) => r.status === 200,
'dashboard response time OK': (r) => r.timings.duration < 300,
'dashboard data complete': (r) => r.json('data.length') > 0,
'dashboard data complete': () => Array.isArray(data) && data.length > 0,
});
errorRate.add(apiResponse.status !== 200);
errorRate.add(!dashboardOK);
responseTimeTrend.add(apiResponse.timings.duration);
}
@@ -150,6 +158,8 @@ function generateHTMLReport(data) {
}
```
Adapt the example's nonempty dashboard-data contract and thresholds to the agreed test dataset and SLO. k6 `check()` records results but needs a threshold to affect the process exit status; HTTP failure metrics alone cannot catch a `200` response with a missing token or malformed payload.
## 🔄 Your Workflow Process
### Step 1: Performance Baseline and Requirements
+21 -4
View File
@@ -95,6 +95,26 @@ export const test = base.extend<{ api: ApiClient }, { workerStorageState: string
### CI: Sharded, Traced, Merge-Blocking (GitHub Actions)
Set artifact capture in Playwright's configuration; an arbitrary environment
variable does not configure the test runner. `retain-on-failure` also captures
the first failure when retries are disabled.
```typescript
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
forbidOnly: !!process.env.CI,
retries: 0, // Stable suite failures block the merge on their first attempt
outputDir: 'test-results',
use: {
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
});
```
```yaml
jobs:
e2e:
@@ -106,9 +126,6 @@ jobs:
- uses: actions/checkout@v4
- run: npm ci && npx playwright install --with-deps chromium
- run: npx playwright test --shard=${{ matrix.shard }}
env:
# trace on first retry: zero overhead on green runs, full forensics on red
PLAYWRIGHT_TRACE: on-first-retry
- uses: actions/upload-artifact@v4
if: failure()
with:
@@ -132,7 +149,7 @@ jobs:
2. **Audit the pyramid**: Push anything provable at unit/API level down the stack. Every E2E test must justify its browser.
3. **Build the foundation before tests**: API-based data factories, worker-scoped auth fixtures, selector conventions, and artifact configuration come first — tests written on sand flake forever.
4. **Write tests to the determinism bar**: Condition-based waits, owned data, role selectors. Run each new test 10x locally (`--repeat-each=10`) before review.
5. **Wire CI as the enforcement point**: Sharding for speed, trace-on-retry for forensics, merge-blocking on the stable suite, and a separate non-blocking lane for quarantined tests.
5. **Wire CI as the enforcement point**: Sharding for speed, retained failure artifacts for forensics, merge-blocking on the stable suite, and a separate non-blocking lane for quarantined tests.
6. **Operate the suite like production**: Weekly review of pass rate, duration trend, and pass-on-retry (flake) rate. Every flake gets a root-cause ticket within 24 hours.
7. **Ratchet quality**: As flakes are fixed, tighten retries downward. The end state is retries=0 and nobody misses them.
+44 -19
View File
@@ -60,6 +60,8 @@ You are **Test Results Analyzer**, an expert test analysis specialist who focuse
### Advanced Test Analysis Framework Example
```python
# Comprehensive test result analysis with statistical modeling
import json
import math
import pandas as pd
import numpy as np
from scipy import stats
@@ -70,34 +72,45 @@ from sklearn.model_selection import train_test_split
class TestResultsAnalyzer:
def __init__(self, test_results_path):
self.test_results = pd.read_json(test_results_path)
# Coverage is a nested report object, not a rectangular DataFrame.
with open(test_results_path, encoding='utf-8') as report:
self.test_results = json.load(report)
if not isinstance(self.test_results, dict):
raise ValueError('Expected one JSON report object')
self.quality_metrics = {}
self.risk_assessment = {}
def analyze_test_coverage(self):
"""Comprehensive test coverage analysis with gap identification"""
coverage = self.test_results.get('coverage')
if not isinstance(coverage, dict):
raise ValueError('Missing coverage object; no coverage claim can be made')
def percentage(section, label):
value = section.get('pct') if isinstance(section, dict) else None
if (isinstance(value, bool) or not isinstance(value, (int, float))
or not math.isfinite(value) or not 0 <= value <= 100):
raise ValueError(f'{label}.pct must be a finite percentage in [0, 100]')
return value
coverage_stats = {
'line_coverage': self.test_results['coverage']['lines']['pct'],
'branch_coverage': self.test_results['coverage']['branches']['pct'],
'function_coverage': self.test_results['coverage']['functions']['pct'],
'statement_coverage': self.test_results['coverage']['statements']['pct']
f'{name[:-1] if name != "branches" else "branch"}_coverage':
percentage(coverage.get(name), name)
for name in ('lines', 'branches', 'functions', 'statements')
}
# Identify coverage gaps
uncovered_files = self.test_results['coverage']['files']
files = coverage.get('files')
if not isinstance(files, dict):
raise ValueError('coverage.files must map paths to coverage objects')
gap_analysis = []
for file_path, file_coverage in uncovered_files.items():
if file_coverage['lines']['pct'] < 80:
gap_analysis.append({
'file': file_path,
'coverage': file_coverage['lines']['pct'],
'risk_level': self._assess_file_risk(file_path, file_coverage),
'priority': self._calculate_coverage_priority(file_path, file_coverage)
})
for file_path, file_coverage in files.items():
if not isinstance(file_coverage, dict):
raise ValueError(f'Invalid coverage object for {file_path}')
line_pct = percentage(file_coverage.get('lines'), file_path)
if line_pct < 80:
gap_analysis.append({'file': file_path, 'coverage': line_pct})
# Coverage gaps identify unexecuted code; attach risk using actual criticality.
return coverage_stats, gap_analysis
def analyze_failure_patterns(self):
"""Statistical analysis of test failures and pattern identification"""
failures = self.test_results['failures']
@@ -187,6 +200,18 @@ class TestResultsAnalyzer:
return report
```
The coverage entry point accepts a JSON object with `coverage.lines`,
`branches`, `functions`, and `statements` each containing a `pct` number, plus
`coverage.files` mapping file paths to objects with `lines.pct`. Missing or
invalid measurements raise an error rather than becoming zero coverage. The
remaining `_...` methods are project-specific adapters to implement before
using prediction, readiness, or reporting paths; coverage percentages alone
cannot supply risk levels or release confidence.
```json
{"coverage":{"lines":{"pct":90},"branches":{"pct":80},"functions":{"pct":95},"statements":{"pct":90},"files":{"src/payment.py":{"lines":{"pct":60}}}}}
```
## 🔄 Your Workflow Process
### Step 1: Data Collection and Validation