Skip to content
Back to skills

Phax Spec

ASecurity

Write or review a spec under docs/specs/ that the phax-planning skill will turn into a plan.md — functional behavior and consumption surface, EARS requirements, testable acceptance criteria, lifecycle and traceability.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 19, 2026
developmentgogitapisecurity

Works with

  • claude code
  • terminal
  • cli
  • api

Security analysis

A100/100

Scanned October 19, 2026

npx -y skills add lbdremy/phax --skill phax-spec --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Phax Spec?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Phax Spec
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lbdremy-phax-spec/badge)](https://www.skillsdirectory.com/skills/lbdremy-phax-spec)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: phax-spec
description: Write or review a spec under docs/specs/ that the phax-planning skill will turn into a plan.md — functional behavior and consumption surface, EARS requirements, testable acceptance criteria, lifecycle and traceability.
---

# phax spec skill

Use this skill when you are asked to write or review a **spec** — the artifact under
`docs/specs/` that captures _what_ a change must do and _why_, before any planning or code.

A phax spec is the **input spec**: it exists to be consumed once — by the phax planning skill
to produce a `plan.md`, and by the executing agents to write tests — then it is archived. The
durable source of truth after that is the code and its tests, never a spec file kept in sync
by hand.

## Where the spec sits in the phax pipeline

```
spec (this skill)  →  plan.md (phax-planning)  →  phax plans lint  →  phax run (extraction + gates + reconciliation)
   what & why            how, decomposed            mechanical check        executable enforcement
```

A spec is **functional behavior and consumption surface**: the _what_ and the _why_, never
the _how_. Technology choices, file layout, module names, and phase decomposition belong to
the plan, not the spec. The one part of the visible shape that DOES belong here is the
**surface** — what the consumer sees and touches (commands, config keys, endpoints, screens);
see the Surface section below. If you find yourself naming functions or sketching internals,
you have left the spec layer.

## Lifecycle

Every spec carries a `status` key in its YAML frontmatter block, drawn from a fixed
set. phax **enforces** it: the value must be one of these exact strings, and a
terminal status (`Abandoned`, `Completed`) must live under `docs/specs/archive/` —
status and location must agree, or validation fails.

```
Draft ──▶ Approved ──▶ Completed
  │           │
  └──▶ Abandoned ◀──┘
```

- **Draft** — under authoring; ambiguities still open; not yet eligible for planning.
- **Approved** — the human approval gate has passed: scope is bounded, requirements are
  unambiguous, acceptance criteria are testable. Only an Approved spec should be planned.
- **Abandoned** — dropped before it shipped (terminal). The file moves to
  `docs/specs/archive/`.
- **Completed** — consumed. The spec has produced a plan and tests; it moves to
  `docs/specs/archive/`. From here on, code + tests are the source of truth. A readable spec
  _view_ can be regenerated from the tests on demand — it is a derived report, not a file to
  maintain.

Move status through its legal transitions with the `phax artifact` command group
(`phax artifact approve|abandon|complete <path>`) rather than hand-editing — it
validates the transition and, on a terminal one, moves the file into `archive/`
for you. Record the status in the frontmatter block (below) and keep it accurate.

`phax artifact approve` also stamps an `approved: { date, baseline }` block into the
frontmatter and writes the spec's own record file, `docs/specs/approvals/<spec file name>.json`. Both are **written by phax,
never by hand** — treat them as machine state, not authored content. Re-approving an
already-**Approved** spec is legal and is the supported way to record an **in-place
revision**: it re-stamps the block and rewrites the record against the spec's current
content. Approve takes `--by operator` or `--by machine:<grant>` (required without a
terminal), and the record names the approver. `phax artifact reopen` returns an Approved
spec to Draft; its bound plans are named and left as they are. Because a plan can only be approved against a recorded, unmodified spec, a plan
whose `Source-Spec` is Approved but **unrecorded** (no record file under `docs/specs/approvals/`) or
**edited since** its approval fails plan approval with exit 12 — re-approve the spec first.
`phax artifact status <spec>` reports the approval date, baseline, and whether the spec has
been edited since.

**Approval needs an empty `## 9. Open questions` section.** `phax artifact approve` refuses
(exit 12) while a question is open, headless or hand-authored, and names `phax artifact decide`;
a hand-authored §9 that does not follow the §9 format (below) is refused too. `phax artifact
decide <spec>` answers the open questions with the operator and moves them to `## 9b`.

## File and naming convention

- One feature = one spec file: `docs/specs/<YYMMDDHHMM>-<slug>.md` — a ten-digit stamp
  (the creation minute in UTC, two zero-padded digits per field) and a slug
  (`[a-z0-9]+(-[a-z0-9]+)*`). The plan it produces mirrors the **slug**, never the
  stamp: `docs/plans/<YYMMDDHHMM>-<slug>-plan.md`, stamped at its own creation.
- Never compute the stamp yourself. Create the file with
  `phax artifact new spec <slug>` — it names the file from the clock and writes the
  frontmatter block below; you fill in the body. Every artifact command refuses a
  file whose name is off-grammar.
- Refer to a spec by its slug in prose, commits and pull requests ("the `plan-prune`
  spec"), never by its stamp. The stamp only orders the directory.
- Keep a spec to **1–3 pages**. If it grows past that, the feature is too big — split it into
  separate specs. A bounded spec is what makes the `Draft → Approved` gate meaningful.

## Canonical spec structure

Start every spec with this frontmatter block, then the ten numbered sections. This is the
shape phax specs converge on; follow it so specs stop drifting. The four authored keys are
exact — an unknown or missing one fails validation. `approved` is the one optional key: it is
written by phax on approval (see Lifecycle), never authored by hand — omit it until the spec
is first approved.

```markdown
---
status: Draft | Approved | Abandoned | Completed
date: YYYY-MM-DD
audience: implementation planning with <agent>
scope: functional behavior and consumption surface
approved:                 # written by phax on approval, never by hand; absent until approved
  date: YYYY-MM-DD
  baseline: <short-sha>
---

# <Title>

## 1. Context

<The situation today. What exists, what the reader needs to know to understand the change.
Quote the relevant config/artifact/behavior. No problem statement yet — just the ground.>

## 2. Problem

<What is wrong or missing. Why the current state is unacceptable. Concrete, specific, and
honest — the problem motivates every requirement below.>

## 3. Product goal

<The change, stated as outcome (what & why), not solution (how). Close with a guiding rule
as a blockquote — the single principle a reader can hold the whole spec against.>

> <One-sentence guiding rule.>

## 4. Terminology

<Define every domain term the spec uses unambiguously. A reader and an agent must read each
term the same way. Omit only if the spec introduces no terms worth pinning.>

## 5. Functional requirements

<The behavioral contract, as numbered subsections (§5.1, §5.2, …). Each requirement is an
EARS statement (see EARS below). Behavior only — no technology, no file names, no code.>

### 5.1 <Requirement title>

<EARS requirement, e.g.> WHEN the user submits an invalid config THE system SHALL reject it
with an actionable message naming the offending field.

## 6. Surface

<The consumption interface of the change, MATERIALIZED — show the artifact, never describe
it in prose. Config: a before → after block with the real fields. API: the actual example
request and response payloads. CLI: the invocation line and an output sketch. Visual UI:
screens/states in text plus a committed design annex. Never internals (modules, functions,
file layout). Mark each block or field **normative** (the name/shape IS the contract; tests
verify it verbatim) or **indicative** (intent binds, the planner may adjust the final
spelling) — an indicative sketch with real fields beats an accurate abstraction. Omit only
if the change has no consumer-visible surface, and say so in one line.>

## 7. Non-goals

<What is explicitly NOT in scope. Bounds the agent's exploration and the plan's surface.
A spec without Non-goals is unbounded — always write this section.>

## 8. Acceptance criteria

<Testable Given/When/Then criteria, one per meaningful behavior, each tracing back to a §5
requirement. Name normative §6 surface elements verbatim in the criteria. These are the seed
of the E2E tests the plan will produce (see Traceability).>

### <Criterion name>

Given <precondition>, when <action>, then <observable outcome>. (refs §5.1)

## 9. Open questions

<Ambiguities you could not resolve, with a recommended default for each. This is the Clarify
gate: surface every unknown here rather than letting the planner guess. When a question is an
arbitration between options, frame each option by its dominant loss (see Arbitration below) —
never a symmetric pros/cons list. Write each question in the §9 format (below), or `None.`
when there are none. Resolve or default each before marking the spec Approved.>

## 9b. Arbitrated questions

<Questions that have been decided, each with its Decision block, or `None.`.>

### The §9 format

A hand-authored spec writes its questions so phax can read them: `phax artifact status` counts
them, and prints the first line that departs from the format when it cannot. Both sections
are h2 headings, and a section ends at the next numbered h2 (`## 10. …`): any other h2 or
heading inside it departs. `None.` alone is an empty section.

```markdown
### <id> — <question>

- <option id> — <label> — abandons: <loss>
- <option id> — <label> — abandons: <loss>

Recommendation: <option id> — <rationale>
```

Ids are runs of characters without whitespace; use short kebab-case ones (`q-keep`, `keep-10`).
Give every option an id: `phax artifact decide` records a verdict by option id, and a
hand-authored §9 without them departs from the format.
The `Recommendation:` and `Decision:` lines name one of the question's own option ids.
An arbitrated question under `## 9b. Arbitrated questions` has the same block, then its
decision:

```markdown
Decision: <option id> — by operator (<name>), <YYYY-MM-DD>
Why: <why>
```

A decision that was lifted from an older spec reads `Decision: <option id> — lifted, unattributed,
<date>`. Earlier decisions follow as `Earlier decision: …` lines, and the decision log is
`## 12. Decision log`, which phax does not read back. A `Decision:` block in `## 9. Open questions`,
an arbitrated question without one, or a bold `**Q2 — …**` heading departs from the format.

## 10. Implementation-planning note

<The handoff to phax-planning: what is settled, what is deliberately left open, and any
constraint the plan must respect. Keep it to the contract — do not start planning here.>
```

## EARS — required for every functional requirement

Every requirement in §5 must follow one of the five EARS (Easy Approach to Requirements
Syntax) patterns. EARS forces each requirement to be atomic and testable — one trigger, one
behavior — which is exactly what makes the acceptance criteria and tests fall out cleanly.

| Pattern          | Template                           | Example                                                               |
| ---------------- | ---------------------------------- | --------------------------------------------------------------------- |
| Ubiquitous       | The system **shall** …             | The system **shall** record the applied security posture per phase.   |
| Event-driven     | **WHEN** … **THE system SHALL** …  | **WHEN** a gate fails THE system SHALL open a same-session fix loop.  |
| State-driven     | **WHILE** … **THE system SHALL** … | **WHILE** a run is rate-limited THE system SHALL show the reset time. |
| Unwanted         | **IF** … **THEN** …                | **IF** a required command is unallowed THEN the preflight SHALL fail. |
| Optional feature | **WHERE** … **THE system SHALL** … | **WHERE** publish is enabled THE system SHALL open a pull request.    |

Rules:

- One requirement = one EARS sentence. If you need "and also", split it into two requirements.
- The subject is the system (phax) or a named component — never "we" or "the developer".
- State the **observable** behavior, not the mechanism. `WHEN config is invalid THE system
SHALL reject it` — not `THE system SHALL call validateConfig()`.
- Group related EARS requirements under a titled `### 5.N` subsection.

## Surface — the consumption contract

**Surface** is the consumption interface of the change: everything a consumer can see or
touch once it ships. It is NOT the _how_ — it is the most observable part of the _what_.
The "what, not how" rule bans **internals** (modules, functions, algorithms, file layout);
it does not ban the surface. Without §6, the acceptance criteria quietly reference an
interface nobody pinned ("when publish is invoked" — invoked _how_?), the planner invents
it, and the `Draft → Approved` gate approves something vaguer than what ships — even though
the surface is exactly the part a human is best placed to judge.

What belongs in §6, by kind of change — and the form it must take:

| Change ships as | Materialize as                                                            |
| --------------- | ------------------------------------------------------------------------- |
| CLI             | the invocation line(s) and an output sketch; config as a before → after block with real keys |
| API             | example request and response payloads (error payload included), real field names |
| UI              | screens/states/actions in text, plus the committed design annex           |
| File format     | an example of the emitted/consumed file, at its user-visible location     |

What never belongs: module paths, function names, internal interfaces, storage layout — an
element earns its place in §6 only if a consumer can observe it from outside.

Rules:

- **Show, don't describe.** Every element appears as the artifact the consumer will see —
  a config block with real field names, a real payload, a command line with its output
  sketch. "The config gains a key (exact name indicative)" in prose, with no block, is the
  vagueness this section exists to kill: write the sketch with real fields and mark it
  indicative instead. When the change replaces an existing surface, show **before → after**,
  quoting the current shape as it really is today.
- **Mark every element normative or indicative.** Normative: the exact name/shape is the
  contract — acceptance criteria cite it verbatim and tests verify it as written. Indicative:
  the intent binds but the planner may adjust the final name/shape. This marking is the
  guard against over-specification — freezing a flag name early is sometimes premature, but
  that must be an explicit choice, never vagueness by omission.
- **Visual UI: text carries structure, an annex carries design.** The section lists screens,
  states, and actions in text; the visual design lives in a design artifact (image, wireframe,
  Figma export) committed next to the spec and referenced from §6, with the same
  normative/indicative marking. The annex is part of the spec — archived with it, spent fuel
  like the rest.
- **No surface, no section?** If the change genuinely has no consumer-visible surface (a pure
  behavioral fix behind an existing interface), say so in one line rather than omitting the
  section silently.

## Acceptance criteria and traceability

Acceptance criteria are the contract made executable. Treat them as the **specification of the
E2E tests** the plan will write.

- Write each criterion in **Given / When / Then** form, against observable behavior. Where a
  §6 surface element is normative, cite it verbatim (``when `phax publish` runs…``), not by
  paraphrase ("when publish is invoked").
- Every criterion **traces back** to a §5 requirement with a `(refs §5.N)` citation. Every §5
  requirement should be covered by at least one criterion — an uncovered requirement is either
  untestable (rewrite it) or unowned (add a criterion).
- The plan that consumes this spec **cites these criteria and §5 requirements** in its phases
  (`refs spec §5.2, AC "…"`), and the agents turn the criteria into E2E tests. This is the
  chain that lets the spec be archived safely: the behavior now lives in tests, and a spec
  _view_ can be decompiled from those tests later. The original intent/rationale that does not
  fit a test (perf budget, security posture, UX feel) lives in the spec's Context/Product goal
  and, after archival, in commits/PRs — not in a maintained spec file.

## Arbitration — decide on the dominant loss

When a spec must choose between viable options, do not weigh symmetric pros/cons lists:
both columns fill endlessly, everything appears to weigh the same, and the list ends up
justifying whichever preference came first. Instead, name — per option — the **dominant
loss**: the strongest thing you abandon by choosing it. The decision becomes a comparison
of losses, and the recommendation states which loss is acceptable and why.

Format, inside a §9 question (the §9 format above):

```markdown
### <id> — <the arbitration>

- <option-a> — <label> — abandons: <the strongest thing this choice gives up>
- <option-b> — <label> — abandons: <the strongest thing this choice gives up>

Recommendation: <option-a> — <why its loss is the acceptable one>.
```

One line per option is enough. If you cannot name what an option abandons, you have not
understood the option yet — that, not a longer pros list, is the signal to dig further.

## Doctrine

- **What, not how.** Functional requirements describe behavior. The moment a requirement names
  a function, a module, a library, or a phase order, it has crossed into the plan's territory —
  move it to the Implementation-planning note as a constraint, or drop it.
- **The surface is what, not how.** Commands, config keys, endpoints, screens are the contract
  the consumer touches — pin them in §6 (normative or indicative), never leave them for the
  planner to invent. "No internals" and "no surface" are different rules; only the first holds.
- **One feature, one spec, 1–3 pages.** Bounded scope is what makes approval meaningful and
  keeps the agent's exploration tight.
- **Every behavior is gate- or test-verifiable.** If an acceptance criterion cannot be checked
  by a test or a gate, the requirement is too vague — sharpen it until it can.
- **No back-compat shims in persisted behavior.** Consistent with phax's schema policy: new
  fields/behaviors are required, removed ones are removed (rejected at validation), not retained
  as ignored-optional. Say so in the spec when it applies.
- **Explicit over permissive.** Prefer per-variant enums and named cases over a permissive
  superset that admits nonsense combinations. Specify the exact allowed set.
- **Arbitrate on what you abandon.** A trade-off is decided by comparing dominant losses,
  not by counting advantages. Name, per option, the strongest thing given up; recommend
  the option whose loss you accept.
- **The spec is spent fuel.** It is consumed once to produce the plan and the tests, then
  archived. Do not design it to be maintained alongside the code.

## Anti-patterns to avoid

- **Over-specification.** "When a spec becomes pseudo-code, you've written the program twice."
  If a requirement reads like an implementation, it is in the wrong layer.
- **Implementation detail in §5.** File names, function signatures, module paths, phase
  ordering — all belong to the plan, not the spec.
- **Untestable acceptance criteria.** "The system should be fast/robust/intuitive" — restate
  as an observable, checkable outcome or cut it.
- **Missing Non-goals.** An unbounded spec invites scope creep in the plan and the run.
- **Unstated surface.** Acceptance criteria that say "when publish is invoked" against a §6
  that never pinned the command: the planner ends up inventing the interface the human never
  approved. Pin it in §6 — or mark it indicative, explicitly.
- **Internals dressed as surface.** Module paths, function signatures, or internal interfaces
  listed in §6. If a consumer cannot observe it from outside, it is not surface — cut it.
- **Prose surface.** A §6 made of descriptions ("a registration exists, form indicative")
  with no materialized block. Described-only surface is still unstated surface — the planner
  ends up inventing the artifact anyway.
- **Unresolved ambiguity smuggled into §5.** Ambiguities go in §9 (Open questions) with a
  recommended default — never hidden as a vague requirement.
- **Symmetric pros/cons.** An advantages/disadvantages list where everything weighs the
  same and the recommendation appears from nowhere. Restate each option as its dominant
  loss and arbitrate on that.
- **False confidence.** Matching a wrong spec satisfies no real requirement. The value is the
  thinking done while writing the spec, not the document itself — interrogate the problem.
- **Free-floating requirements.** A §5 requirement with no acceptance criterion, or a criterion
  with no `refs §`, breaks the traceability chain that lets the spec be archived.

## Headless authoring

Everything above describes the Markdown spec you write in an interactive session. When the
session is spawned by `phax artifact new spec <slug> --headless --brief <file|->`, the
deliverable changes: you return the **spec document** — a JSON object — and phax renders it
deterministically to the canonical structure above, writes the `.md` with its JSON sidecar
beside it, and commits both. The doctrine does not change; only the container does.

- **The JSON object is your final message**, alone: no code fence, no commentary before or
  after it. The prompt carries the authoritative JSON Schema; `phax artifact schema spec`
  prints the same schema.
- **Write no files.** phax writes, renders and commits; a file you write is not the artifact.
- **Every key is required.** An empty section is an empty array or an explicit variant
  (`before: null`, `docsPage.kind: "none"`), never a missing key. Unknown keys are rejected.
- **Plain text, not Markdown structure.** Headings, numbering, `refs §` citations and the
  `None.` placeholders are rendered by phax.

Top-level keys, with the section each renders to:

| Key                  | Shape                                                        | Renders to                               |
| -------------------- | ------------------------------------------------------------ | ---------------------------------------- |
| `version`, `kind`    | `1`, `"spec"`                                                | —                                        |
| `title`              | string                                                       | `# <Title>`                              |
| `ground`             | `[{ path, note }]` — files you read to write §1              | §1, a "Ground read:" list                |
| `context`            | string                                                       | §1 Context                               |
| `problem`            | string                                                       | §2 Problem                               |
| `productGoal`        | `{ statement, guidingRule }`                                 | §3, the rule as a blockquote             |
| `terminology`        | `[{ term, definition }]`                                     | §4 Terminology                           |
| `requirements`       | non-empty `[{ id, title, pattern, statement }]`              | §5, one `### 5.N` per requirement        |
| `surface`            | `[{ surface, binding, before, after }]`                      | §6 Surface                               |
| `nonGoals`           | `[string]`                                                   | §7 Non-goals                             |
| `acceptanceCriteria` | non-empty `[{ id, name, given, when, then, refs }]`          | §8, "Given …, when …, then …" + refs     |
| `openQuestions`      | `[{ id, question, options, recommendation, rationale }]`     | §9 Open questions                        |
| `arbitratedQuestions`| `[]` — required, and must be empty                           | §9b Arbitrated questions                 |
| `planningNote`       | `{ settled, open, constraints }` — three string arrays       | §10 Implementation-planning note         |
| `docsPage`           | `{ kind: "page", page, reader, example }` or `{ kind: "none", why }` | §11 Docs page                    |

The constrained shapes:

- **`pattern`** — the requirement's EARS pattern: `"ubiquitous"`, `"event"`, `"state"`,
  `"unwanted"` or `"optional"` (see EARS above). `statement` is the full EARS sentence.
- **`surface`** — the element's kind and name, prefixed by one of `cli: `, `config: `,
  `file: `, `api: `, `package: `, `internal: ` (e.g. `"cli: phax artifact schema"`).
- **`binding`** — `"normative"` or `"indicative"` (see Surface above).
- **`before` / `after`** — the materialized element: `after` is the invocation, block or
  payload the consumer sees; `before` is its current form, or `null` for a new element.
- **Open questions** — each has two or more `options` of `{ id, label, abandons }`
  (`abandons` is the option's dominant loss — see Arbitration), and `recommendation` is the
  `id` of one of them, argued in `rationale`.
- **`arbitratedQuestions`** — required and always `[]`: an authoring session leaves every
  question open, and a non-empty array is refused as an invalid document. Each question renders
  under its id, with option ids on its bullets and its recommendation.

Traceability is checked when phax decodes the document, and the first violation rejects it:
requirement, question and per-question option ids are unique; every acceptance criterion's
`refs` names existing requirement `id`s; every requirement is referenced by at least one
criterion; every `recommendation` names one of its question's options. Requirement ids are
yours to choose — phax numbers the §5 headings by position and rewrites refs to match.

## Reviewing a spec

When asked to review rather than write, check, in order:

1. **Layer** — is anything in §5 actually _how_ (implementation)? Move or cut it.
2. **EARS** — does every §5 requirement follow a pattern, atomically?
3. **Surface** — is §6 present (or explicitly declared empty)? Is every element MATERIALIZED
   (before → after config block, example payload, invocation + output sketch) rather than
   described in prose, consumer-observable, and marked normative/indicative? Does a visual UI
   reference a committed design annex? Do the acceptance criteria cite normative elements
   verbatim?
4. **Coverage** — does every §5 requirement have an acceptance criterion, and does every
   criterion `refs §`?
5. **Bounds** — are Non-goals present and honest? Is the spec ≤ 3 pages?
6. **Clarity gate** — are all §9 open questions resolved or defaulted before `Approved`?
   Does every arbitration name the dominant loss per option, rather than listing pros/cons?
7. **Testability** — could an agent turn every acceptance criterion into a passing test
   without inventing intent the spec did not state?

## Example well-formed slice

```markdown
---
status: Approved
date: 2026-06-12
audience: implementation planning with Claude Code
scope: functional behavior and consumption surface
---

# Push Branch and Create Pull Request

## 3. Product goal

phax must be able to publish a finished run: push the run's branch and open a pull request
whose body is the run review handoff, so review happens on GitHub, not in the terminal.

> phax never publishes a run that has not passed its gates.

## 5. Functional requirements

### 5.1 Publish gating

WHEN a run has completed with all gates green THE system SHALL make the run eligible to publish.

### 5.2 Pull request creation

WHERE publishing is enabled THE system SHALL push the run branch and open a pull request whose
body is the run review handoff.

### 5.3 Idempotency

IF a pull request already exists for the run branch THEN the system SHALL update it rather than
open a duplicate.

## 6. Surface

New command (normative):

    phax publish <run-id>

Refusal, on a red gate (exit code and named gate normative; wording indicative):

    ✗ publish refused: gate "test" is red
    $? = 1

`phax.json`, before → after (key spelling indicative; presence of an enable switch normative
per §5.2 WHERE clause):

    "publish": { "auto": false }          →    "publish": { "auto": true }

Pull request: body is the run review handoff (normative); title format indicative.

## 8. Acceptance criteria

### Gates gate publishing

Given a run with a red gate, when `phax publish <run-id>` runs, then it exits non-zero and
names the failing gate. (refs §5.1)

### PR carries the handoff

Given a completed green run with publishing enabled, when `phax publish <run-id>` runs, then
a pull request exists whose body is the run review handoff. (refs §5.2)

### Re-publish is idempotent

Given a run already published, when `phax publish <run-id>` runs again, then the existing
pull request is updated and no duplicate is created. (refs §5.3)
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.