Skip to content

Commit ba2df8c

Browse files
authored
Run documentation agents through the native executor (#104)
* Run documentation agents through native executor * Update Docs Agent native workflow contract * Update Docs Agent producer contract * Fix CI actionlint setup * Run actionlint as a CI gate * Document CI validation status
1 parent 2f216e0 commit ba2df8c

6 files changed

Lines changed: 159 additions & 99 deletions

File tree

.github/workflows/ci.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- trunk
8+
- feat/native-docs-agent
9+
10+
permissions:
11+
contents: read
12+
13+
jobs:
14+
validate:
15+
name: Test, Build, and Verify
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
- uses: actions/checkout@v4
20+
with:
21+
repository: Automattic/docs-agent
22+
ref: 5344a4bfbda4a0553cc92636258e46a715b1c72d
23+
path: .producer/docs-agent
24+
- uses: actions/checkout@v4
25+
with:
26+
repository: Automattic/wp-codebox
27+
ref: 54c2f9a7bc3cd1fe20055d496c83efcfb99afb41
28+
path: .producer/wp-codebox
29+
- uses: pnpm/action-setup@v4
30+
with:
31+
version: 10.8.1
32+
- uses: actions/setup-node@v4
33+
with:
34+
node-version: 22
35+
cache: pnpm
36+
- uses: rhysd/actionlint@v1.7.12
37+
- run: pnpm install --frozen-lockfile
38+
- run: pnpm test
39+
env:
40+
DOCS_AGENT_DIR: ${{ github.workspace }}/.producer/docs-agent
41+
WP_CODEBOX_DIR: ${{ github.workspace }}/.producer/wp-codebox
42+
- run: pnpm build
43+
- run: pnpm verify

.github/workflows/developer-docs-agent.yml

Lines changed: 5 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -24,69 +24,7 @@ jobs:
2424
run_kind: ${{ github.event_name == 'workflow_dispatch' && 'bootstrap' || 'maintenance' }}
2525
base_ref: trunk
2626
docs_branch: docs-agent/build-with-wordpress-developer-docs
27-
docs_agent_ref: main
2827
writable_paths: README.md,docs/**,plugins/**/README.md
29-
bootstrap_contract: |
30-
{
31-
"required_paths": [
32-
"README.md",
33-
"docs/README.md",
34-
"docs/architecture.md",
35-
"docs/generated-outputs.md",
36-
"docs/skills-and-integrations.md",
37-
"docs/contributor-workflows.md"
38-
],
39-
"required_globs": [
40-
{
41-
"pattern": "docs/**/*.md",
42-
"min": 5
43-
}
44-
],
45-
"entry_points": [
46-
{
47-
"path": "README.md",
48-
"must_link_to": ["docs/README.md"]
49-
},
50-
{
51-
"path": "docs/README.md",
52-
"must_link_to": [
53-
"docs/architecture.md",
54-
"docs/generated-outputs.md",
55-
"docs/skills-and-integrations.md",
56-
"docs/contributor-workflows.md"
57-
]
58-
}
59-
],
60-
"forbidden_phrases": [
61-
"future coverage",
62-
"deferred",
63-
"saved for later"
64-
]
65-
}
66-
context_repositories: |
67-
[
68-
{
69-
"repo": "Automattic/studio",
70-
"ref": "trunk",
71-
"alias": "studio",
72-
"paths": [
73-
"apps/cli/ai/tools/**",
74-
"apps/cli/ai/system-prompt.ts",
75-
"apps/cli/commands/mcp.ts",
76-
"eval/promptfoo.config.yaml"
77-
]
78-
},
79-
{
80-
"repo": "WordPress/agent-skills",
81-
"ref": "trunk",
82-
"alias": "wordpress-agent-skills",
83-
"paths": [
84-
"skills/**",
85-
"docs/packaging.md",
86-
".github/workflows/ai-skill-maintenance.yml"
87-
]
88-
}
89-
]
9028
verification_commands: |
9129
[
9230
"pnpm install --frozen-lockfile",
@@ -103,9 +41,9 @@ jobs:
10341
prompt: |
10442
Maintain developer-facing documentation for Build with WordPress.
10543
106-
Manual dispatch runs the bootstrap flow and should create or improve the initial developer documentation structure if the repository does not already have one. Push-triggered merge checks run the maintenance flow and should make the smallest source-grounded documentation update needed for newly merged code or finish with no changes when documentation is current.
107-
108-
Write the developer documentation as repository docs: `docs/README.md` as the index, focused topic pages under `docs/**`, and `plugins/**/README.md` only when a plugin package needs local developer-facing contract notes. For the bootstrap surface, prefer `docs/architecture.md`, `docs/generated-outputs.md`, `docs/skills-and-integrations.md`, and `docs/contributor-workflows.md` as the main topic pages.
44+
Manual dispatch runs the native technical documentation bootstrap package. Push-triggered runs use the native technical documentation maintenance package and should make the smallest source-grounded documentation update needed for newly merged code or finish with no changes when documentation is current.
10945
110-
Cover the repository purpose, package layout, generated plugin artifacts, build and verification commands, skill packaging, Studio integration points, and contributor workflows. Document agent/plugin configuration files and generated plugin artifacts from those repository docs and plugin README files. Use the read-only context aliases `studio` and `wordpress-agent-skills` only as source evidence for integration behavior.
111-
secrets: inherit
46+
Write repository documentation with `docs/README.md` as the index, focused topic pages under `docs/**`, and `plugins/**/README.md` only when a plugin package needs local developer-facing contract notes. Cover the repository purpose, package layout, generated plugin artifacts, build and verification commands, skill packaging, Studio integration points, and contributor workflows.
47+
secrets:
48+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
49+
EXTERNAL_PACKAGE_SOURCE_POLICY: ${{ secrets.EXTERNAL_PACKAGE_SOURCE_POLICY }}

.github/workflows/skills-agent.yml

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ permissions:
1010
pull-requests: write
1111
issues: write
1212

13+
concurrency:
14+
group: skills-agent-${{ github.ref }}
15+
cancel-in-progress: false
16+
1317
jobs:
1418
skills-agent:
1519
name: Maintain Live Skills
@@ -18,32 +22,7 @@ jobs:
1822
audience: skills
1923
base_ref: trunk
2024
docs_branch: docs-agent/build-with-wordpress-skills
21-
docs_agent_ref: main
2225
writable_paths: skills/**,plugins/**/skills/**,plugins/**/README.md
23-
context_repositories: |
24-
[
25-
{
26-
"repo": "Automattic/studio",
27-
"ref": "trunk",
28-
"alias": "studio",
29-
"paths": [
30-
"apps/cli/ai/tools/**",
31-
"apps/cli/ai/system-prompt.ts",
32-
"apps/cli/commands/mcp.ts",
33-
"eval/promptfoo.config.yaml"
34-
]
35-
},
36-
{
37-
"repo": "WordPress/agent-skills",
38-
"ref": "trunk",
39-
"alias": "wordpress-agent-skills",
40-
"paths": [
41-
"skills/**",
42-
"docs/packaging.md",
43-
".github/workflows/ai-skill-maintenance.yml"
44-
]
45-
}
46-
]
4726
verification_commands: |
4827
[
4928
"pnpm install --frozen-lockfile",
@@ -57,4 +36,10 @@ jobs:
5736
"description": "Generated skill package outputs are committed after build"
5837
}
5938
]
60-
secrets: inherit
39+
prompt: |
40+
Maintain live agent skill instructions and generated packaged skill outputs for Build with WordPress.
41+
42+
Run the native skills maintenance package and make the smallest source-grounded skill update needed for newly merged code, or finish with no changes when the skills are current. Edit live skill instructions under `skills/**`, packaged skill outputs under `plugins/**/skills/**`, and `plugins/**/README.md` only when a plugin package needs local developer-facing contract notes.
43+
secrets:
44+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
45+
EXTERNAL_PACKAGE_SOURCE_POLICY: ${{ secrets.EXTERNAL_PACKAGE_SOURCE_POLICY }}

docs/contributor-workflows.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,18 +147,16 @@ The repository includes GitHub Actions workflows for documentation maintenance a
147147
- `developer-docs-agent.yml` runs the technical documentation agent lane for bootstrap and maintenance documentation updates.
148148
- `skills-agent.yml` runs the skills maintenance lane.
149149

150-
Manual documentation bootstrap runs should create or improve the initial documentation structure. Push-triggered merge checks should make the smallest source-grounded documentation update needed for newly merged code or finish with no changes when docs are current.
150+
Manual documentation bootstrap runs should create or improve the initial documentation structure. Push-triggered maintenance runs should make the smallest source-grounded documentation update needed for newly merged code or finish with no changes when docs are current.
151151

152152
### Developer docs workflow contract
153153

154-
`developer-docs-agent.yml` calls the reusable `Automattic/docs-agent/.github/workflows/maintain-docs.yml` workflow for the technical documentation lane. The workflow source defines the documentation maintenance boundary that this repository expects reviewers to enforce:
154+
`developer-docs-agent.yml` calls the reusable `Automattic/docs-agent/.github/workflows/maintain-docs.yml` workflow for the technical documentation lane. The caller grants its `github.token` the publication permissions declared by the workflow and explicitly forwards `OPENAI_API_KEY` for live runs and the required `EXTERNAL_PACKAGE_SOURCE_POLICY` package allowlist.
155155

156156
- `workflow_dispatch` runs with `run_kind: bootstrap`; pushes to `trunk` run with `run_kind: maintenance`.
157157
- `docs_branch` is `docs-agent/build-with-wordpress-developer-docs` and `base_ref` is `trunk`.
158158
- writable documentation paths are limited to `README.md`, `docs/**`, and `plugins/**/README.md`.
159-
- the bootstrap contract requires the top-level README, `docs/README.md`, and the four topic pages `docs/architecture.md`, `docs/generated-outputs.md`, `docs/skills-and-integrations.md`, and `docs/contributor-workflows.md`.
160-
- the contract also requires at least five Markdown files under `docs/**`, a README link to `docs/README.md`, docs-index links to each required topic page, and avoidance of backlog-style phrases such as `future coverage`, `deferred`, and `saved for later`.
161-
- read-only context evidence is scoped to the `studio` and `wordpress-agent-skills` aliases for Studio MCP behavior and skill packaging patterns.
159+
- Docs Agent selects its native package from the immutable reusable-workflow revision GitHub resolves for the run. The technical bootstrap lane requires a published PR; maintenance permits a no-change result.
162160
- verification commands are `pnpm install --frozen-lockfile`, `pnpm build`, and `pnpm verify`; the drift check is `git diff --exit-code` so generated package outputs must be committed after a build.
163161

164162
When changing the docs workflow, keep this contract aligned with the repository documentation structure in [the docs index](README.md). When changing generated-output behavior, update the docs and generated files in the same pull request so maintenance runs can finish cleanly.
@@ -167,6 +165,8 @@ When changing the docs workflow, keep this contract aligned with the repository
167165

168166
`skills-agent.yml` uses the same reusable Docs Agent workflow with `audience: skills`. It is triggered manually and on a weekly Monday schedule, writes only to `skills/**`, generated skill copies under `plugins/**/skills/**`, and plugin README files, and runs the same install, build, verify, and generated-output drift checks. Keep this workflow focused on live skill content; generated package structure and developer documentation belong in the technical docs workflow and the generator/verifier source.
169167

168+
The `CI` workflow provides automated validation for pull requests and configured branch pushes. It checks out Docs Agent at `5344a4bfbda4a0553cc92636258e46a715b1c72d` and WP Codebox at `54c2f9a7bc3cd1fe20055d496c83efcfb99afb41`, then runs `pnpm test`, `pnpm build`, and `pnpm verify`. The workflow contract test rejects any producer revision or schema drift. This repository does not currently require the workflow as a merge gate.
169+
170170
## Pull request checklist
171171

172172
Before requesting review:

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"build": "node scripts/build-telemetry-mcp.mjs && node scripts/build-plugins.mjs",
99
"build:telemetry-mcp": "node scripts/build-telemetry-mcp.mjs",
1010
"export:cursor": "node scripts/export-cursor-plugin.mjs",
11-
"verify": "node scripts/verify-plugins.mjs"
11+
"verify": "node scripts/verify-plugins.mjs",
12+
"test": "node tests/docs-agent-workflows.test.mjs"
1213
},
1314
"dependencies": {
1415
"@modelcontextprotocol/sdk": "^1.29.0",
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import assert from "node:assert/strict"
2+
import { execFileSync } from "node:child_process"
3+
import { readFile } from "node:fs/promises"
4+
import { resolve } from "node:path"
5+
6+
const root = resolve(new URL("..", import.meta.url).pathname)
7+
const docsAgentRevision = "5344a4bfbda4a0553cc92636258e46a715b1c72d"
8+
const wpCodeboxRevision = "54c2f9a7bc3cd1fe20055d496c83efcfb99afb41"
9+
const docsAgentDir = process.env.DOCS_AGENT_DIR
10+
const wpCodeboxDir = process.env.WP_CODEBOX_DIR
11+
12+
assert.ok(docsAgentDir, "DOCS_AGENT_DIR must point to the pinned Docs Agent checkout")
13+
assert.ok(wpCodeboxDir, "WP_CODEBOX_DIR must point to the pinned WP Codebox checkout")
14+
15+
const revision = (directory) => execFileSync("git", ["-C", directory, "rev-parse", "HEAD"], { encoding: "utf8" }).trim()
16+
const readJson = async (directory, path) => JSON.parse(await readFile(resolve(directory, path), "utf8"))
17+
18+
assert.equal(revision(docsAgentDir), docsAgentRevision, "Docs Agent checkout must match the declared producer revision")
19+
assert.equal(revision(wpCodeboxDir), wpCodeboxRevision, "WP Codebox checkout must match the Docs Agent producer revision")
20+
21+
const docsAgentWorkflow = await readFile(resolve(docsAgentDir, ".github/workflows/maintain-docs.yml"), "utf8")
22+
const wpCodeboxContract = await readJson(wpCodeboxDir, "contracts/run-agent-task-reusable-workflow-interface.v1.json")
23+
assert.equal(wpCodeboxContract.schema, "wp-codebox/reusable-workflow-interface/v1")
24+
assert.match(docsAgentWorkflow, new RegExp(`uses: Automattic/wp-codebox/.github/workflows/run-agent-task.yml@${wpCodeboxRevision}`))
25+
assert.match(docsAgentWorkflow, /DOCS_AGENT_REVISION: \$\{\{ github\.job_workflow_sha \}\}/)
26+
assert.match(docsAgentWorkflow, /OPENAI_API_KEY: \$\{\{ secrets\.OPENAI_API_KEY \}\}/)
27+
assert.match(docsAgentWorkflow, /ACCESS_TOKEN: \$\{\{ github\.token \}\}/)
28+
assert.match(docsAgentWorkflow, /EXTERNAL_PACKAGE_SOURCE_POLICY: \$\{\{ secrets\.EXTERNAL_PACKAGE_SOURCE_POLICY \}\}/)
29+
30+
const docsAgentInputsBlock = docsAgentWorkflow.match(/ inputs:\n([\s\S]*?) secrets:/)?.[1]
31+
const docsAgentSecretsBlock = docsAgentWorkflow.match(/ secrets:\n([\s\S]*?) outputs:/)?.[1]
32+
assert.ok(docsAgentInputsBlock, "Docs Agent must declare reusable-workflow inputs")
33+
assert.ok(docsAgentSecretsBlock, "Docs Agent must declare reusable-workflow secrets")
34+
const docsAgentInputs = [...docsAgentInputsBlock.matchAll(/^ ([a-z_]+):/gm)].map((match) => match[1])
35+
const docsAgentSecrets = [...docsAgentSecretsBlock.matchAll(/^ ([A-Z_]+):/gm)].map((match) => match[1])
36+
const producerSecrets = Object.keys(wpCodeboxContract.secrets)
37+
const requiredProducerSecrets = Object.entries(wpCodeboxContract.secrets)
38+
.filter(([, definition]) => definition.required)
39+
.map(([name]) => name)
40+
const workflows = [
41+
{
42+
path: ".github/workflows/developer-docs-agent.yml",
43+
audience: "technical",
44+
runKind: true,
45+
writablePaths: "README.md,docs/**,plugins/**/README.md",
46+
},
47+
{
48+
path: ".github/workflows/skills-agent.yml",
49+
audience: "skills",
50+
runKind: false,
51+
writablePaths: "skills/**,plugins/**/skills/**,plugins/**/README.md",
52+
},
53+
]
54+
55+
for (const workflow of workflows) {
56+
const source = await readFile(resolve(root, workflow.path), "utf8")
57+
const withBlock = source.match(/ with:\n([\s\S]*?) secrets:/)?.[1]
58+
const secretsBlock = source.match(/ secrets:\n([\s\S]*)$/)?.[1]
59+
assert.ok(withBlock, `${workflow.path} must call the producer with inputs`)
60+
assert.ok(secretsBlock, `${workflow.path} must forward producer secrets`)
61+
const usedInputs = [...withBlock.matchAll(/^ ([a-z_]+):/gm)].map((match) => match[1])
62+
const usedSecrets = [...secretsBlock.matchAll(/^ ([A-Z_]+):/gm)].map((match) => match[1])
63+
64+
assert.match(source, /uses: Automattic\/docs-agent\/.github\/workflows\/maintain-docs.yml@main/)
65+
assert.deepEqual(usedInputs, [...new Set(usedInputs)], `${workflow.path} must not declare an input twice`)
66+
assert.ok(usedInputs.every((input) => docsAgentInputs.includes(input)), `${workflow.path} uses an input absent from the Docs Agent schema`)
67+
assert.deepEqual(usedSecrets, ["OPENAI_API_KEY", "EXTERNAL_PACKAGE_SOURCE_POLICY"], `${workflow.path} must forward the Docs Agent secrets`)
68+
assert.ok(usedSecrets.every((secret) => docsAgentSecrets.includes(secret)), `${workflow.path} forwards a secret absent from the Docs Agent schema`)
69+
assert.ok(usedSecrets.every((secret) => producerSecrets.includes(secret) || secret === "EXTERNAL_PACKAGE_SOURCE_POLICY"), `${workflow.path} forwards a secret absent from the producer schema`)
70+
assert.ok(requiredProducerSecrets.includes("EXTERNAL_PACKAGE_SOURCE_POLICY"), "WP Codebox must require the external package policy")
71+
assert.match(source, new RegExp(`audience: ${workflow.audience}`))
72+
assert.match(source, new RegExp(`writable_paths: ${workflow.writablePaths.replaceAll("*", "\\*")}`))
73+
assert.match(source, /base_ref: trunk/)
74+
assert.match(source, /docs_branch: docs-agent\/build-with-wordpress-/)
75+
assert.match(source, /pnpm install --frozen-lockfile/)
76+
assert.match(source, /pnpm build/)
77+
assert.match(source, /pnpm verify/)
78+
assert.match(source, /git diff --exit-code/)
79+
assert.match(source, /permissions:\n contents: write\n pull-requests: write\n issues: write/)
80+
assert.match(source, /OPENAI_API_KEY: \$\{\{ secrets\.OPENAI_API_KEY \}\}/)
81+
assert.match(source, /EXTERNAL_PACKAGE_SOURCE_POLICY: \$\{\{ secrets\.EXTERNAL_PACKAGE_SOURCE_POLICY \}\}/)
82+
assert.doesNotMatch(source, /ACCESS_TOKEN/)
83+
assert.doesNotMatch(source, /docs_agent_ref/)
84+
assert.doesNotMatch(source, /runtime-agent-full-run|runtime_(?:provider|profile|profiles|execution|dependencies|task|config)|datamachine|homeboy/i)
85+
86+
if (workflow.runKind) {
87+
assert.match(source, /run_kind: \$\{\{ github\.event_name == 'workflow_dispatch' && 'bootstrap' \|\| 'maintenance' \}\}/)
88+
} else {
89+
assert.doesNotMatch(source, /^ run_kind:/m)
90+
}
91+
}
92+
93+
console.log("Docs Agent and WP Codebox producer contract validation passed")

0 commit comments

Comments
 (0)