Wizard enforces Goal and Draft Goal description compliance on GitHub issues.
When a Goal or Draft Goal is opened, edited, or assignees change, the app
normalizes allowed sections, creates a spec file when needed (Goals and Draft
Goals on opened), and surfaces warnings in the issue description.
Some organizations are listed under Spec-Disabled Organizations in
config/whitelist.json. For those orgs, Wizard skips # Spec validation,
automatic spec file creation, and related other-sections warnings. Deadline and
Stakeholders Interview checks still run.
See Whitelist for configuration and full behavior.
A Goal description may use these sections:
# Spec
https://github.com/<org>/<repo>/blob/main/docs/specs/<name>.md
# Deadline
ETA: undefined
# Stakeholders Interview
- https://docs.google.com/document/d/...
Custom H1 sections are also allowed for grouping issue or PR references —
for example # Blocked by followed by a list of issue links. The section
body must contain only issue or PR references; free-form text is not
permitted inside custom sections. Instructions, scope notes, and design
decisions belong in the linked spec file, not in the Goal description.
# Spec is always required.
.md file under
docs/specs/ on the main branch (flat or nested paths are allowed).https://github.com/<org>/<repo>/blob/main/docs/specs/<name>.mdhttps://github.com/<org>/<repo>/blob/main/docs/specs/<folder>/<name>.md# Deadline is always present on Goal and Draft Goal issues. Wizard creates it
automatically if missing.
ETA: line: ETA: undefined or
ETA: <DD-MMM-YYYY> (for example, ETA: 25-May-2026). Wizard normalizes that
line on each validation run.ETA: undefined when no completion date is set yet.# Deadline are left in place, but Wizard adds a warning
tagging the last human editor (same as invalid content in other sections).@holdex goal set-eta <date> or @holdex goal set-eta +5d
(see Commands).# Deadline
first, then replaces the existing ETA: line inside that section (including
ETA: undefined or invalid values). It does not append a duplicate line.ETA: line in
the description, or appends one if none exists.Use this section to link Google Documents for stakeholder discovery.
https://docs.google.com/document/d/...).# Spec link.App-managed > [!WARNING] blocks are ignored during validation so the
app's own writes never trigger a violation.
When a Goal is opened and has no valid spec URL under # Spec,
Wizard:
Derives a file name from the Goal title: strips the Goal: prefix,
lowercases, replaces spaces and non-alphanumeric characters with hyphens,
truncates to 50 characters.
Example: Goal: Spec workflow → spec-workflow.md.
If that file already exists in docs/specs/, appends -2, -3, and so on
until an unused name is found.
Commits docs/specs/<feature>.md to main with frontmatter only:
---
goal: <goal-issue-url>
---
Updates the Goal description with the permanent file URL under # Spec.
The link is permanent. If the Goal title changes later, the file name is not updated — the existing link continues to point to the correct file.
Spec creation runs only on opened, not on later edits, so retries do not
create duplicate files.
[!NOTE]
@holdex goal create-specalways creates a flat file atdocs/specs/<name>.md. Nested paths underdocs/specs/are valid only when you link to an existing file manually.
docs/specs/<feature>.md starts as frontmatter-only and grows as scope is
defined. As the Goal is implemented, sections graduate to the relevant file
in docs/ and are removed from the spec file. A spec may graduate to
multiple files if its sections cover different areas.
When all sections have graduated, the spec file is kept but left as
frontmatter-only — it is never deleted. This ensures the # Spec link in
the Goal description never breaks and the Goal can be reopened without
re-creating the file.
Warnings are written to a dedicated Warnings section at the end of the description. They are removed and re-evaluated on each validation run.
| Condition | Who is mentioned |
|---|---|
No valid # Spec URL |
Current assignees |
Spec URL is not a docs/specs/**/*.md link on main |
Current assignees |
Extra content in # Spec |
Last human editor |
Invalid format in # Stakeholders Interview |
Last human editor |
Non–Google Doc URL in # Stakeholders Interview |
Last human editor |
Invalid ETA: value in # Deadline (not undefined or DD-MMM-YYYY) |
Last human editor |
Assignees present but ETA: undefined in # Deadline |
Current assignees |
ETA: date in # Deadline is in the past |
Current assignees |
Extra content in # Deadline (non-ETA: lines) |
Last human editor |
Orphan lines between main sections (no new # title line) |
Last human editor |
Custom # section is empty or its body is not issue/PR references only (Invalid Section Warning) |
Last human editor |
Example (missing spec):
> [!WARNING]
> @assignee1 @assignee2 this Goal has no linked Spec. A Spec is required before work can begin.
> See the [contributing guidelines](https://github.com/holdex/developers/blob/main/docs/CONTRIBUTING.md#specs)
> or the [Wizard docs](https://wizard.holdex.io/docs/commands) for how to create one.
Warnings clear automatically once the description is fixed.
Validation is skipped when the event sender is the app bot or an excluded user, so the app does not tag itself or re-process its own updates.
Create or attach a Google Document under # Stakeholders Interview via
issue comments. See Commands for syntax.
| Command | Action |
|---|---|
@holdex goal create-spec |
Creates docs/specs/<name>.md and links it under # Spec |
@holdex goal create-google-doc |
Creates a new Google Doc and appends its URL to the section |
@holdex goal attach-google-doc <url> |
Attaches an existing Google Doc URL |
create-google-doc and attach-google-doc append the Google Doc URL even when
the section still has validation warnings; fix any remaining warning in the
description when you can.
Google Doc folder routing for the browser extension is configured in Spec Document Folder Configuration.
When Wizard commits a spec file to main, the commit may be rejected by a
branch protection rule or repository ruleset. Wizard handles this automatically:
Repo-level rulesets — Wizard calls GET /repos/{owner}/{repo}/rules/branches/main
to find active rules of type pull_request or update that originate from a
repository-level ruleset. For each one, Wizard fetches the full ruleset and adds
itself as a bypass actor (bypass_mode: always), then retries the commit.
Classic branch protection — Wizard calls
GET /repos/{owner}/{repo}/branches/main/protection. If the rule includes
required_pull_request_reviews or restrictions, Wizard adds its app slug to
the relevant allowlist and retries the commit.
Organisation-level rulesets — If the blocking rule originates from an organisation ruleset, Wizard cannot modify it. Instead it posts a comment on the Goal issue tagging the last editor:
@editor The Wizard app could not auto-create the spec file because a branch protection rule on
mainis set at the organization level and cannot be modified by the app.An org admin must add Wizard as a bypass actor: https://github.com/organizations/{org}/settings/rules/{id}
After adding the bypass, re-trigger spec creation with
@holdex goal create-spec.
If the specific ruleset URL cannot be determined, the comment provides manual navigation instructions instead.
[!NOTE] Wizard only attempts the bypass fix when the commit fails with HTTP 409 or 422. If branch protection is not configured on
main, no API calls are made.
Goal validations run on these issue events:
openededitedassignedreopenedIssues with GitHub type Goal (Goal: title prefix) or Draft Goal
([DRAFT] Goal: title prefix) are processed. Draft Goals receive the same
section normalization and description warnings.