APM Spec Guardian -- Four-Panel Advisory Review for OpenAPM
This skill institutionalizes the two-round adversarial spec review
that produced OpenAPM v0.1 by hand. The panel is FAN-OUT +
SYNTHESIZER. Each panelist runs in its own agent thread (via the
task tool) and returns JSON matching
assets/panelist-return-schema.json. The orchestrator schema-validates
each return, hands all returns to the spec-editor-synthesizer
(also a task thread, returns JSON matching
assets/synthesizer-return-schema.json), runs the linter checklist
in assets/linter-checklist.md, then renders ONE comment from
assets/comment-template.md.
This skill is ADVISORY by design. It does not compute a binary
verdict, it does not apply verdict labels, and it does not gate
merge. The panel surfaces findings; the maintainer ships.
Activation scope
This skill activates ONLY when the PR diff touches at least one of:
docs/src/content/docs/specs/openapm-*.md (the normative spec
artifact, current and future versions)
docs/src/content/docs/specs/schemas/*.schema.json (sidecar JSON
Schemas, if/when the inline Appendix-A schemas are extracted to
files)
tests/fixtures/spec-conformance/** (the conformance fixture seed)
Edits to any OTHER documentation page MUST NOT trigger this skill.
The maintainer's general docs-sync skill covers those.
Architecture invariants
- Advisory regime, not gate regime. There is no
APPROVE /
REJECT, no spec-approved / spec-rejected label, no
deterministic verdict computation. The synthesizer returns a
ship_decision (fold_and_ship / needs_revision /
next_brief); this is prose for the human reviewer, never
auto-applied as a label or status check.
- Ship-meter floor.
ship_decision: fold_and_ship REQUIRES
shocked_meter_avg >= 7.0. Below 7.0 the synthesizer MUST emit
needs_revision (single drafter pass on the existing artifact)
or next_brief (another round of panel review with a new brief).
The floor is advisory wording in the comment, not a status check.
- Blocker veto. If ANY panelist returns
new_blocking_findings[].length > 0, the synthesizer MUST emit
ship_decision: next_brief regardless of the shocked_meter_avg.
A blocking finding from one panel is not outweighed by three
panels rating the artifact 9/10.
- Single-writer interlock. Only the orchestrator writes to the
PR: exactly one
add-comment call and one remove-labels call.
The remove-labels call sweeps spec-review (trigger
idempotency). NO add-labels call -- there are no verdict labels.
Panelist subagents and the synthesizer subagent return JSON only
and MUST NOT call any gh write command, post comments, apply
labels, or touch PR state.
- Single-emission discipline. Exactly one comment per panel run,
rendered from
assets/comment-template.md after all subagents
return and the linter checklist runs.
- ASCII-only artifact. Every byte the skill writes (the comment,
the synthesizer prose, any rendered fold instruction) MUST be
within printable ASCII (U+0020 - U+007E). The skill inherits the
repo encoding rule from
.github/instructions/encoding.instructions.md
(if present) and additionally enforces it on the spec artifact via
linter check 1.
- No-vendor-foundation language ban. The spec artifact MUST NOT
contain "CNCF", "Linux Foundation", "Sandbox", "Incubation",
"W3C Process", or "IETF RFC stream". The persona prompts MAY
reference these as pedigree (a panelist's credibility comes from
having edited OpenAPI; that does not put OpenAPI's foundation
affiliation in the spec text). Linter check 2 greps the artifact
for the forbidden token list AFTER any fold.
Agent roster
| Agent |
Role |
Always active? |
| Swagger / OpenAPI Editor |
Interface-contract discipline (schemas, $ref hygiene, oneOf discriminators, conformance enumeration) |
Yes |
| OCI Distribution Editor |
Registry-HTTP rigor (hash envelopes, mirror tolerance, fail-closed extraction, supply-chain threat model) |
Yes |
| Package-Manager Registry-Contract Editor |
Dependency-resolution rigor (semver dialect pinning, lockfile determinism, transitive conflict policy, reserved-slot defensive MUSTs) |
Yes |
| W3C TAG Architect |
Web-platform integration / architecture (extensibility, layering, fingerprinting, machine-readable contract surface) |
Yes |
| Spec Editor Synthesizer |
Same hand that drafted; aggregates panel returns, computes shocked_meter_avg, clusters convergent themes, produces fold-now / defer / reject lists and ship_decision |
Yes |
The roster is invariant for the v0.1 lineage. Changing it requires
bumping the skill version.
Topology
apm-spec-guardian SKILL (orchestrator thread)
|
+------ Wave 0: scope decision (orchestrator-internal) ------+
| classify diff: |
| - editorial-only (tiny patch, single section, no schema |
| change, no fixture add) -> skip Wave 3, run Wave 5 |
| linter only, render lightweight comment |
| - editorial-patch (default for PR-trigger) |
| -> skip Wave 1+2, start at Wave 3 |
| - new-version (operator opt-in via PR body marker |
| `apm-spec-guardian: new-version`) |
| -> run Wave 1 + 2 + 3 + 4 + 5 |
+------------------------------------------------------------+
|
IF new-version mode: run Wave 1 + Wave 2 first
|
Wave 1 (optional, new-version only): task -> spec-editor-synthesizer
acting as ASSESSOR -- reads issue context + corpus +
produces SPEC_BRIEF_v0 (session-state artifact)
Wave 2 (optional, new-version only): task -> spec-editor-synthesizer
acting as DRAFTER -- produces SPEC_DRAFT_v0 from SPEC_BRIEF_v0
|
Wave 3: FAN-OUT via task tool (4 panelists in parallel)
+------+------+--------+------+
v v v v
swagger oci pkgmgr tag
(each returns JSON per assets/panelist-return-schema.json)
|
v <-- S4 schema-validate per return
v <-- on malformed: re-spawn that panelist
v (max 2 attempts; then placeholder)
v
Wave 4: task -> spec-editor-synthesizer
- aggregates findings across 4 panels
- computes shocked_meter_avg
- resolves dissent
- clusters into convergent themes
- emits fold_now[] + defer_v0_1_1[] + defer_v0_2[] + reject[]
- emits ship_decision honoring ship-meter floor + blocker veto
- returns assets/synthesizer-return-schema.json
|
v <-- S4 schema-validate
v
Wave 5: LINTER (mechanical, from assets/linter-checklist.md)
- 11 checks; each MUST pass
- failures append to ship_prose as advisory notes
- linter does NOT change ship_decision; it informs the
human reviewer
|
Wave 6: orchestrator (sole writer)
| |
v v
add-comment remove-labels
(max:1) [spec-review]
(trigger idempotency reset)
Wave 0 -- scope decision
The orchestrator classifies the diff before spawning any panelist.
Decision rules, in order:
- New-version mode. If the PR body contains the literal marker
line
apm-spec-guardian: new-version, OR the diff creates a new
docs/src/content/docs/specs/openapm-*.md file (not an edit to
an existing one), classify as new-version. Run Waves 1 -> 2 ->
3 -> 4 -> 5 -> 6.
- Editorial-only mode. If ALL of the following hold:
- the diff added < 50 lines AND removed < 50 lines total across
all in-scope paths,
- no JSON Schema file was added, removed, or had its top-level
properties keys changed,
- no fixture file was added or removed (existing fixture
content edits are OK),
- no anchor of the form
<a id="req- was added or removed,
classify as editorial-only. SKIP Wave 3 + Wave 4. Run Wave 5
linter on the modified artifact. Render the lightweight
editorial-only branch of assets/comment-template.md (just the
linter result + a one-line "no substantive spec change
detected").
- Editorial-patch mode (default). Everything else. Run Wave 3
-> 4 -> 5 -> 6. Wave 1 + Wave 2 are SKIPPED; the panel reviews
the existing artifact as modified by the PR diff.
Document the decision in the comment header (one line: "Scope: ;
diff = +X/-Y lines across N files").
Wave 3 -- panel fan-out
Spawn the following four tasks in PARALLEL via the task tool, one
task per persona:
spec-swagger-editor
spec-oci-editor
spec-pkgmgr-editor
spec-tag-architect
Each task prompt MUST:
- Reference its persona file by relative path
(
../../agents/spec-<slug>.agent.md) so the subagent loads its
own scope, lens, and pedigree.
- Include the PR number, title, body, and full diff (passed inline),
PLUS the current contents of the in-scope spec artifact AS
MODIFIED by the diff (the panel reviews the post-merge state of
the file, not just the diff).
- Cite
assets/panelist-return-schema.json and require the
subagent to emit JSON matching that schema as its FINAL message.
- State the calibrated severity contract: "Use
new_blocking_findings ONLY for issues that would break a
conformant implementation, leak a security guarantee, or invalidate
a published normative claim. Use new_recommended_findings for
substantive improvements. Use new_nit_findings for one-line
editorial polish. The panel is advisory; nothing you return blocks
merge; pick the severity that honestly matches your signal
strength."
- State the no-vendor-foundation rule: "Your pedigree as a [persona]
is part of your prompt. Do NOT propose adding the names of any
standards body, foundation, or governance program (CNCF, Linux
Foundation, Sandbox, Incubation, W3C Process, IETF RFC stream) to
the spec artifact text. Findings that recommend such additions
will be auto-rejected by the synthesizer."
- State the ASCII rule: "Every byte in your return JSON and every
byte of any proposed fix or replacement text MUST be within U+0020
- U+007E. No emojis, no Unicode dashes, no curly quotes."
- Pass
round (1 on first pass, 2+ on subsequent rounds in
new-version mode).
- Restate the output contract: NO
gh write commands, NO posting
comments, NO label changes, NO touching PR state. JSON return only.
Wave 4 -- synthesizer
Pass all four validated panelist JSON returns to a task invocation
that loads ../../agents/spec-editor-synthesizer.agent.md. The prompt
MUST:
- Provide all panelist returns as structured input.
- Ask for:
convergence_table, convergent_themes (themes flagged
by 2+ panels), fold_now[] (surgical single-section fixes only),
defer_v0_1_1[] (small patches deferrable to next patch
release), defer_v0_2[] (architectural work requiring a reserved
slot in a future major), reject[] (findings the synthesizer
declines, with rationale), ship_decision, ship_prose,
linter_handoff_notes.
- State the ship-decision rules verbatim:
- If
sum(panelist.new_blocking_findings) > 0 across all panels,
ship_decision MUST be next_brief.
- Else if
shocked_meter_avg < 7.0, ship_decision MUST be
needs_revision.
- Else
ship_decision MAY be fold_and_ship.
- State the no-vendor-foundation rule: auto-reject any
panelist-proposed fix that would add a banned token to the
artifact; surface in
reject[] with rationale.
- Cite
assets/synthesizer-return-schema.json and require JSON
return.
- Restate the contract: the panel is advisory. The synthesizer does
NOT pick a verdict label. The
ship_decision is prose for the
human reviewer, not a gate. NO gh write commands.
Validate the synthesizer return against
assets/synthesizer-return-schema.json. On failure, re-spawn once
with the violation cited.
Wave 5 -- linter
Run assets/linter-checklist.md against the in-scope artifact set
(spec markdown + schemas + fixtures). The checklist has 11 mechanical
checks; each is a one-liner producing exit code 0 (or empty grep
output where noted). Record pass / fail per check.
Linter outcomes are ADVISORY: a failed check does NOT change the
synthesizer's ship_decision. It DOES surface in the comment as a
"Linter notes" section so the maintainer can decide whether to fold
the fix into the same PR. If ship_decision == fold_and_ship AND
any linter check failed, the comment surfaces the conflict
prominently ("Synthesizer recommends ship; linter found N issues
worth folding first").
Wave 6 -- render the comment
Load assets/comment-template.md, fill the placeholders from the
synthesizer + panelist JSON + linter results, and emit it as exactly
ONE comment.
Filling rules:
- The convergence table renders ONE row per panelist with verdict,
shocked_meter, new_blockers, new_recommended, new_nits counts.
- The fold-now list renders the synthesizer's
fold_now[]
verbatim, ordered as returned.
- The defer-v0.1.1 and defer-v0.2 lists render below the fold list,
collapsed in
<details> blocks if either has more than 3 items.
- The reject list renders only if non-empty.
- The "Linter notes" section renders only if any check failed; each
failed check renders the check id + the one-line failure summary.
- Full per-panel findings collapse into a
<details> at the bottom.
- NEVER render the words "Verdict", "APPROVE", "REJECT", "blocked",
"merge gate", or any equivalent. The panel is advisory.
Then sweep the spec-review label via safe-outputs.remove-labels
(idempotent on missing labels). NO add-labels call.
Output contract (non-negotiable)
- Exactly ONE comment per panel run, rendered from
assets/comment-template.md.
- Exactly ONE
remove-labels call sweeping [spec-review].
- NO
add-labels call.
- Subagents (panelists + synthesizer) NEVER write to PR state,
NEVER call
gh pr comment, NEVER call gh pr edit --add-label.
They return JSON. The orchestrator is the sole writer.
- ASCII-only across every byte the orchestrator writes.
- Never invent new top-level template sections or drop existing
ones.
Loop budget
- Editorial-patch mode: at most 2 panel rounds. If round 2 still
carries new_blocking_findings, the synthesizer emits
ship_decision: next_brief with a ship_prose note that the
loop budget is exhausted and the maintainer should ESCALATE
(manually decide between drafting a fix or closing the PR).
- New-version mode: at most 3 panel rounds. Same exhaustion
semantics on round 3.
- Editorial-only mode: zero panel rounds (linter only).
The orchestrator increments and tracks the round counter; subagents
receive it as input but MUST NOT trust panel-side memory.
Gotchas
- Roster invariant. The frontmatter description, the activation
scope list, the roster table, the topology diagram, and the
schema enum MUST agree on the 4 panelists + 1 synthesizer. If you
change one, change all in the same edit.
- Blocker veto trumps ship-meter. A panelist who returns one
new_blocking_finding and a shocked_meter of 9 means "the spec is
mostly excellent but this one thing would break a conformant
implementation". That blocker still vetoes fold_and_ship. Do not
let the synthesizer average it away.
- Calibrated severity discipline. The advisory regime relies on
panelists distinguishing blocking from recommended honestly. If a
panelist marks every editorial nit as blocking, the synthesizer's
blocker veto becomes a denial-of-ship. The panelist prompts state
the contract explicitly; the synthesizer arbitration prose is the
safety valve.
- Wave 0 editorial-only is a noise filter, not a quality
shortcut. It exists so a one-line typo fix to a fixture comment
does not summon four expert agents. It MUST NOT fire on schema
changes, anchor additions, or fixture-tree topology changes; the
rules above are intentionally conservative.
- No-vendor-foundation tokens may appear in panelist returns (a
panelist can name "the W3C TAG" as their pedigree in their
summary field) but MUST NOT appear in the synthesizer's
fold_now[].patch_instruction or in the rendered comment body
outside <details> collapsed sections. The synthesizer auto-rejects
panelist proposals that would add banned tokens to the artifact.
- ASCII enforcement is per-byte, not per-codepoint. A character
with codepoint > 0x7E is non-ASCII even if it would round-trip
through a different encoding. The linter checks raw byte values,
not the rendered visual.
- Subagent write enforcement is contract-based, not sandbox-based.
Tool permissions are workflow-scoped, not subagent-scoped, so
every spawned task technically inherits the same
gh toolset.
The "subagents must not write" rule is enforced by the prompt
contract in each .agent.md plus the safe-outputs.add-comment.max: 1 fail-soft.
- Spec drift across count sites. Linter check 6 catches when sec.
1.3 sentence, Appendix C trailer, and Appendix D revision-history
disagree on the normative-statement total. This is the most common
failure mode of a fold pass and is the reason the linter is
mandatory before render.
Relationship to apm-review-panel
apm-review-panel is the general OSS multi-persona review for any
non-trivial PR in the repo. apm-spec-guardian is its narrow,
spec-only sibling: a different persona roster, a different ship
decision schema (shocked_meter instead of stance enum), and a
mandatory linter step. The architectural shape (FAN-OUT +
SYNTHESIZER + single-writer interlock + advisory regime) is
deliberately the same so a contributor reading one can read the
other. Do not merge them; the persona pedigrees and the artifact
type (spec vs code) are different enough that one-size-fits-all
prompts would dilute both.
1---2name: apm-spec-guardian3description: Use this skill to run a four-panel adversarial advisory review on any pull request that touches the OpenAPM specification artifact (docs/src/content/docs/specs/openapm-*.md), its inline / sidecar JSON Schemas (docs/src/content/docs/specs/schemas/*.schema.json), or the conformance fixture seed (tests/fixtures/spec-conformance/**). The panel fans out to four spec-ecosystem reviewers (swagger-openapi-editor, oci-distribution-editor, pkgmgr-registry-contract-editor, w3c-tag-architect), each running in its own agent thread, and a spec-editor synthesizer that produces a fold-now / defer-v0.1.1 / defer-v0.2 / reject list plus a ship decision keyed off a 1..10 shocked_meter scale. The orchestrator is the sole writer to the PR: ONE consolidated comment, no verdict labels, no merge gating. The panel is advisory -- it surfaces findings, prioritizes folds, and renders a ship recommendation that the maintainer weighs.4---56# APM Spec Guardian -- Four-Panel Advisory Review for OpenAPM78This skill institutionalizes the two-round adversarial spec review9that produced OpenAPM v0.1 by hand. The panel is FAN-OUT +10SYNTHESIZER. Each panelist runs in its own agent thread (via the11`task` tool) and returns JSON matching12`assets/panelist-return-schema.json`. The orchestrator schema-validates13each return, hands all returns to the `spec-editor-synthesizer`14(also a task thread, returns JSON matching15`assets/synthesizer-return-schema.json`), runs the linter checklist16in `assets/linter-checklist.md`, then renders ONE comment from17`assets/comment-template.md`.1819This skill is ADVISORY by design. It does not compute a binary20verdict, it does not apply verdict labels, and it does not gate21merge. The panel surfaces findings; the maintainer ships.2223## Activation scope2425This skill activates ONLY when the PR diff touches at least one of:2627- `docs/src/content/docs/specs/openapm-*.md` (the normative spec28 artifact, current and future versions)29- `docs/src/content/docs/specs/schemas/*.schema.json` (sidecar JSON30 Schemas, if/when the inline Appendix-A schemas are extracted to31 files)32- `tests/fixtures/spec-conformance/**` (the conformance fixture seed)3334Edits to any OTHER documentation page MUST NOT trigger this skill.35The maintainer's general `docs-sync` skill covers those.3637## Architecture invariants3839- **Advisory regime, not gate regime.** There is no `APPROVE` /40 `REJECT`, no `spec-approved` / `spec-rejected` label, no41 deterministic verdict computation. The synthesizer returns a42 `ship_decision` (`fold_and_ship` / `needs_revision` /43 `next_brief`); this is prose for the human reviewer, never44 auto-applied as a label or status check.45- **Ship-meter floor.** `ship_decision: fold_and_ship` REQUIRES46 `shocked_meter_avg >= 7.0`. Below 7.0 the synthesizer MUST emit47 `needs_revision` (single drafter pass on the existing artifact)48 or `next_brief` (another round of panel review with a new brief).49 The floor is advisory wording in the comment, not a status check.50- **Blocker veto.** If ANY panelist returns51 `new_blocking_findings[].length > 0`, the synthesizer MUST emit52 `ship_decision: next_brief` regardless of the shocked_meter_avg.53 A blocking finding from one panel is not outweighed by three54 panels rating the artifact 9/10.55- **Single-writer interlock.** Only the orchestrator writes to the56 PR: exactly one `add-comment` call and one `remove-labels` call.57 The `remove-labels` call sweeps `spec-review` (trigger58 idempotency). NO `add-labels` call -- there are no verdict labels.59 Panelist subagents and the synthesizer subagent return JSON only60 and MUST NOT call any `gh` write command, post comments, apply61 labels, or touch PR state.62- **Single-emission discipline.** Exactly one comment per panel run,63 rendered from `assets/comment-template.md` after all subagents64 return and the linter checklist runs.65- **ASCII-only artifact.** Every byte the skill writes (the comment,66 the synthesizer prose, any rendered fold instruction) MUST be67 within printable ASCII (U+0020 - U+007E). The skill inherits the68 repo encoding rule from `.github/instructions/encoding.instructions.md`69 (if present) and additionally enforces it on the spec artifact via70 linter check 1.71- **No-vendor-foundation language ban.** The spec artifact MUST NOT72 contain "CNCF", "Linux Foundation", "Sandbox", "Incubation",73 "W3C Process", or "IETF RFC stream". The persona prompts MAY74 reference these as pedigree (a panelist's credibility comes from75 having edited OpenAPI; that does not put OpenAPI's foundation76 affiliation in the spec text). Linter check 2 greps the artifact77 for the forbidden token list AFTER any fold.7879## Agent roster8081| Agent | Role | Always active? |82|-------|------|----------------|83| [Swagger / OpenAPI Editor](../../agents/spec-swagger-editor.agent.md) | Interface-contract discipline (schemas, $ref hygiene, oneOf discriminators, conformance enumeration) | Yes |84| [OCI Distribution Editor](../../agents/spec-oci-editor.agent.md) | Registry-HTTP rigor (hash envelopes, mirror tolerance, fail-closed extraction, supply-chain threat model) | Yes |85| [Package-Manager Registry-Contract Editor](../../agents/spec-pkgmgr-editor.agent.md) | Dependency-resolution rigor (semver dialect pinning, lockfile determinism, transitive conflict policy, reserved-slot defensive MUSTs) | Yes |86| [W3C TAG Architect](../../agents/spec-tag-architect.agent.md) | Web-platform integration / architecture (extensibility, layering, fingerprinting, machine-readable contract surface) | Yes |87| [Spec Editor Synthesizer](../../agents/spec-editor-synthesizer.agent.md) | Same hand that drafted; aggregates panel returns, computes shocked_meter_avg, clusters convergent themes, produces fold-now / defer / reject lists and ship_decision | Yes |8889The roster is invariant for the v0.1 lineage. Changing it requires90bumping the skill version.9192## Topology9394```95 apm-spec-guardian SKILL (orchestrator thread)96 |97 +------ Wave 0: scope decision (orchestrator-internal) ------+98 | classify diff: |99 | - editorial-only (tiny patch, single section, no schema |100 | change, no fixture add) -> skip Wave 3, run Wave 5 |101 | linter only, render lightweight comment |102 | - editorial-patch (default for PR-trigger) |103 | -> skip Wave 1+2, start at Wave 3 |104 | - new-version (operator opt-in via PR body marker |105 | `apm-spec-guardian: new-version`) |106 | -> run Wave 1 + 2 + 3 + 4 + 5 |107 +------------------------------------------------------------+108 |109 IF new-version mode: run Wave 1 + Wave 2 first110 |111 Wave 1 (optional, new-version only): task -> spec-editor-synthesizer112 acting as ASSESSOR -- reads issue context + corpus +113 produces SPEC_BRIEF_v0 (session-state artifact)114 Wave 2 (optional, new-version only): task -> spec-editor-synthesizer115 acting as DRAFTER -- produces SPEC_DRAFT_v0 from SPEC_BRIEF_v0116 |117 Wave 3: FAN-OUT via task tool (4 panelists in parallel)118 +------+------+--------+------+119 v v v v120 swagger oci pkgmgr tag121 (each returns JSON per assets/panelist-return-schema.json)122 |123 v <-- S4 schema-validate per return124 v <-- on malformed: re-spawn that panelist125 v (max 2 attempts; then placeholder)126 v127 Wave 4: task -> spec-editor-synthesizer128 - aggregates findings across 4 panels129 - computes shocked_meter_avg130 - resolves dissent131 - clusters into convergent themes132 - emits fold_now[] + defer_v0_1_1[] + defer_v0_2[] + reject[]133 - emits ship_decision honoring ship-meter floor + blocker veto134 - returns assets/synthesizer-return-schema.json135 |136 v <-- S4 schema-validate137 v138 Wave 5: LINTER (mechanical, from assets/linter-checklist.md)139 - 11 checks; each MUST pass140 - failures append to ship_prose as advisory notes141 - linter does NOT change ship_decision; it informs the142 human reviewer143 |144 Wave 6: orchestrator (sole writer)145 | |146 v v147 add-comment remove-labels148 (max:1) [spec-review]149 (trigger idempotency reset)150```151152## Wave 0 -- scope decision153154The orchestrator classifies the diff before spawning any panelist.155Decision rules, in order:1561571. **New-version mode.** If the PR body contains the literal marker158 line `apm-spec-guardian: new-version`, OR the diff creates a new159 `docs/src/content/docs/specs/openapm-*.md` file (not an edit to160 an existing one), classify as `new-version`. Run Waves 1 -> 2 ->161 3 -> 4 -> 5 -> 6.1622. **Editorial-only mode.** If ALL of the following hold:163 - the diff added < 50 lines AND removed < 50 lines total across164 all in-scope paths,165 - no JSON Schema file was added, removed, or had its top-level166 `properties` keys changed,167 - no fixture file was added or removed (existing fixture168 content edits are OK),169 - no anchor of the form `<a id="req-` was added or removed,170 classify as `editorial-only`. SKIP Wave 3 + Wave 4. Run Wave 5171 linter on the modified artifact. Render the lightweight172 editorial-only branch of `assets/comment-template.md` (just the173 linter result + a one-line "no substantive spec change174 detected").1753. **Editorial-patch mode (default).** Everything else. Run Wave 3176 -> 4 -> 5 -> 6. Wave 1 + Wave 2 are SKIPPED; the panel reviews177 the existing artifact as modified by the PR diff.178179Document the decision in the comment header (one line: "Scope: <mode>;180diff = +X/-Y lines across N files").181182## Wave 3 -- panel fan-out183184Spawn the following four tasks in PARALLEL via the `task` tool, one185task per persona:186187- `spec-swagger-editor`188- `spec-oci-editor`189- `spec-pkgmgr-editor`190- `spec-tag-architect`191192Each task prompt MUST:193194- Reference its persona file by relative path195 (`../../agents/spec-<slug>.agent.md`) so the subagent loads its196 own scope, lens, and pedigree.197- Include the PR number, title, body, and full diff (passed inline),198 PLUS the current contents of the in-scope spec artifact AS199 MODIFIED by the diff (the panel reviews the post-merge state of200 the file, not just the diff).201- Cite `assets/panelist-return-schema.json` and require the202 subagent to emit JSON matching that schema as its FINAL message.203- State the calibrated severity contract: "Use204 `new_blocking_findings` ONLY for issues that would break a205 conformant implementation, leak a security guarantee, or invalidate206 a published normative claim. Use `new_recommended_findings` for207 substantive improvements. Use `new_nit_findings` for one-line208 editorial polish. The panel is advisory; nothing you return blocks209 merge; pick the severity that honestly matches your signal210 strength."211- State the no-vendor-foundation rule: "Your pedigree as a [persona]212 is part of your prompt. Do NOT propose adding the names of any213 standards body, foundation, or governance program (CNCF, Linux214 Foundation, Sandbox, Incubation, W3C Process, IETF RFC stream) to215 the spec artifact text. Findings that recommend such additions216 will be auto-rejected by the synthesizer."217- State the ASCII rule: "Every byte in your return JSON and every218 byte of any proposed fix or replacement text MUST be within U+0020219 - U+007E. No emojis, no Unicode dashes, no curly quotes."220- Pass `round` (1 on first pass, 2+ on subsequent rounds in221 new-version mode).222- Restate the output contract: NO `gh` write commands, NO posting223 comments, NO label changes, NO touching PR state. JSON return only.224225## Wave 4 -- synthesizer226227Pass all four validated panelist JSON returns to a `task` invocation228that loads `../../agents/spec-editor-synthesizer.agent.md`. The prompt229MUST:230231- Provide all panelist returns as structured input.232- Ask for: `convergence_table`, `convergent_themes` (themes flagged233 by 2+ panels), `fold_now[]` (surgical single-section fixes only),234 `defer_v0_1_1[]` (small patches deferrable to next patch235 release), `defer_v0_2[]` (architectural work requiring a reserved236 slot in a future major), `reject[]` (findings the synthesizer237 declines, with rationale), `ship_decision`, `ship_prose`,238 `linter_handoff_notes`.239- State the ship-decision rules verbatim:240 - If `sum(panelist.new_blocking_findings)` > 0 across all panels,241 `ship_decision` MUST be `next_brief`.242 - Else if `shocked_meter_avg < 7.0`, `ship_decision` MUST be243 `needs_revision`.244 - Else `ship_decision` MAY be `fold_and_ship`.245- State the no-vendor-foundation rule: auto-reject any246 panelist-proposed fix that would add a banned token to the247 artifact; surface in `reject[]` with rationale.248- Cite `assets/synthesizer-return-schema.json` and require JSON249 return.250- Restate the contract: the panel is advisory. The synthesizer does251 NOT pick a verdict label. The `ship_decision` is prose for the252 human reviewer, not a gate. NO `gh` write commands.253254Validate the synthesizer return against255`assets/synthesizer-return-schema.json`. On failure, re-spawn once256with the violation cited.257258## Wave 5 -- linter259260Run `assets/linter-checklist.md` against the in-scope artifact set261(spec markdown + schemas + fixtures). The checklist has 11 mechanical262checks; each is a one-liner producing exit code 0 (or empty grep263output where noted). Record pass / fail per check.264265Linter outcomes are ADVISORY: a failed check does NOT change the266synthesizer's `ship_decision`. It DOES surface in the comment as a267"Linter notes" section so the maintainer can decide whether to fold268the fix into the same PR. If `ship_decision == fold_and_ship` AND269any linter check failed, the comment surfaces the conflict270prominently ("Synthesizer recommends ship; linter found N issues271worth folding first").272273## Wave 6 -- render the comment274275Load `assets/comment-template.md`, fill the placeholders from the276synthesizer + panelist JSON + linter results, and emit it as exactly277ONE comment.278279Filling rules:280281- The convergence table renders ONE row per panelist with verdict,282 shocked_meter, new_blockers, new_recommended, new_nits counts.283- The fold-now list renders the synthesizer's `fold_now[]`284 verbatim, ordered as returned.285- The defer-v0.1.1 and defer-v0.2 lists render below the fold list,286 collapsed in `<details>` blocks if either has more than 3 items.287- The reject list renders only if non-empty.288- The "Linter notes" section renders only if any check failed; each289 failed check renders the check id + the one-line failure summary.290- Full per-panel findings collapse into a `<details>` at the bottom.291- NEVER render the words "Verdict", "APPROVE", "REJECT", "blocked",292 "merge gate", or any equivalent. The panel is advisory.293294Then sweep the `spec-review` label via `safe-outputs.remove-labels`295(idempotent on missing labels). NO `add-labels` call.296297## Output contract (non-negotiable)298299- Exactly ONE comment per panel run, rendered from300 `assets/comment-template.md`.301- Exactly ONE `remove-labels` call sweeping `[spec-review]`.302- NO `add-labels` call.303- Subagents (panelists + synthesizer) NEVER write to PR state,304 NEVER call `gh pr comment`, NEVER call `gh pr edit --add-label`.305 They return JSON. The orchestrator is the sole writer.306- ASCII-only across every byte the orchestrator writes.307- Never invent new top-level template sections or drop existing308 ones.309310## Loop budget311312- **Editorial-patch mode:** at most 2 panel rounds. If round 2 still313 carries new_blocking_findings, the synthesizer emits314 `ship_decision: next_brief` with a `ship_prose` note that the315 loop budget is exhausted and the maintainer should ESCALATE316 (manually decide between drafting a fix or closing the PR).317- **New-version mode:** at most 3 panel rounds. Same exhaustion318 semantics on round 3.319- **Editorial-only mode:** zero panel rounds (linter only).320321The orchestrator increments and tracks the round counter; subagents322receive it as input but MUST NOT trust panel-side memory.323324## Gotchas325326- **Roster invariant.** The frontmatter description, the activation327 scope list, the roster table, the topology diagram, and the328 schema enum MUST agree on the 4 panelists + 1 synthesizer. If you329 change one, change all in the same edit.330- **Blocker veto trumps ship-meter.** A panelist who returns one331 `new_blocking_finding` and a shocked_meter of 9 means "the spec is332 mostly excellent but this one thing would break a conformant333 implementation". That blocker still vetoes `fold_and_ship`. Do not334 let the synthesizer average it away.335- **Calibrated severity discipline.** The advisory regime relies on336 panelists distinguishing blocking from recommended honestly. If a337 panelist marks every editorial nit as blocking, the synthesizer's338 blocker veto becomes a denial-of-ship. The panelist prompts state339 the contract explicitly; the synthesizer arbitration prose is the340 safety valve.341- **Wave 0 editorial-only is a noise filter, not a quality342 shortcut.** It exists so a one-line typo fix to a fixture comment343 does not summon four expert agents. It MUST NOT fire on schema344 changes, anchor additions, or fixture-tree topology changes; the345 rules above are intentionally conservative.346- **No-vendor-foundation tokens may appear in panelist returns** (a347 panelist can name "the W3C TAG" as their pedigree in their348 `summary` field) but MUST NOT appear in the synthesizer's349 `fold_now[].patch_instruction` or in the rendered comment body350 outside `<details>` collapsed sections. The synthesizer auto-rejects351 panelist proposals that would add banned tokens to the artifact.352- **ASCII enforcement is per-byte, not per-codepoint.** A character353 with codepoint > 0x7E is non-ASCII even if it would round-trip354 through a different encoding. The linter checks raw byte values,355 not the rendered visual.356- **Subagent write enforcement is contract-based, not sandbox-based.**357 Tool permissions are workflow-scoped, not subagent-scoped, so358 every spawned task technically inherits the same `gh` toolset.359 The "subagents must not write" rule is enforced by the prompt360 contract in each `.agent.md` plus the `safe-outputs.add-comment.max:361 1` fail-soft.362- **Spec drift across count sites.** Linter check 6 catches when sec.363 1.3 sentence, Appendix C trailer, and Appendix D revision-history364 disagree on the normative-statement total. This is the most common365 failure mode of a fold pass and is the reason the linter is366 mandatory before render.367368## Relationship to apm-review-panel369370`apm-review-panel` is the general OSS multi-persona review for any371non-trivial PR in the repo. `apm-spec-guardian` is its narrow,372spec-only sibling: a different persona roster, a different ship373decision schema (shocked_meter instead of stance enum), and a374mandatory linter step. The architectural shape (FAN-OUT +375SYNTHESIZER + single-writer interlock + advisory regime) is376deliberately the same so a contributor reading one can read the377other. Do not merge them; the persona pedigrees and the artifact378type (spec vs code) are different enough that one-size-fits-all379prompts would dilute both.