BookingZone Spec Automation — How It Triggers¶
This document explains, end to end, what fires on every Issue change,
PR, and merge, and how specs stay synchronised across
reservation-api-server, admin-web-app, venuplus-app,
customer-web-app, and this repo.
If you're new to the platform: read README.md,
constitution/system-constitution.md,
and PROJECT-MANAGEMENT.md first.
The mental model in 1 sentence¶
Issues drive lifecycle. PRs drive code. Specs capture intent. The automation keeps all three in sync without humans having to remember.
GitHub Issues + ProjectV2 are the lifecycle source of truth. Branches and PRs are implementation artifacts. Spec frontmatter status is a projection of Issue state, not branch state.
Process-guard caller contract¶
Process-guard workflows (branch-name-guard, pr-auto-tracking-issue,
spec-discipline) are reusable workflows in this repo. Consumer repos
call them, but triggers (on:) must be defined in the caller, not the
reusable.
To avoid burning Actions minutes on PRs that don't need guards (e.g. drafts
into integration/* branches), callers MUST:
- Filter to canonical branches:
branches: [develop, main] - Use minimal event types (see table in linked doc)
- Set
concurrency: cancel-in-progress: truekeyed by PR number
Full pattern and copy-paste snippets:
docs/PROCESS-GUARD-CALLERS.md
Canonical policy: ADR-0012
Triggers and what they do¶
0. Every Issue opened with a spec template → issue-lifecycle.yml¶
When an Issue with label spec:local, spec:trace, or spec:master
is opened:
issue-to-spec-reusable.ymlruns in the consuming repo- Picks the next free spec ID (
NNNfor local/trace,SYS-NNNfor master) - Creates branch
feat/issue-NNN-<slug> - Scaffolds
specs/NNN-<slug>/spec.mdwith frontmatter pre-filled (status: Draft, linked_issues: [#NNN], constitution_version) - Adds
plan.mdandtasks.mdplaceholders - Pushes the branch and opens a PR linked to the Issue
- The PR is the contract for the spec content; review/merge as normal
This means: opening an Issue is the only action a team member takes to start a new feature. Everything else cascades.
0b. Every Issue status / state change → issue-lifecycle.yml¶
Triggers: issues:[edited|closed|reopened] and projects_v2_item:[edited].
Calls issue-status-sync-reusable.yml:
- Resolves the spec path from the Issue body (or stored mapping)
- Reads the Project Status field value
- Maps Project Status → spec frontmatter status (table below)
- Updates the spec.md frontmatter and commits to default branch with
[skip ci]
Mapping:
| Project Status | Spec status |
|---|---|
| Backlog / Refining / Ready | Draft |
| In Progress / In Code Review | InProgress |
| In QA | InQA |
| Done | Completed |
| Cancelled | Cancelled |
| Blocked | (no change) |
(Issue closed completed) |
Completed |
(Issue closed not_planned) |
Cancelled |
A. Every PR opened in any repo → spec-discipline.yml¶
Calls the reusable workflow
spec-discipline-reusable.yml
in this repo. It runs five checks:
| # | Check | Action |
|---|---|---|
| 1 | Spec frontmatter lint | Every spec under specs/ must have valid YAML frontmatter (spec_id, type, status, master_spec_path for traces, etc.). Tool: tools/lint_specs.py. Hard fail. |
| 2 | Platform-version match | Repo's AGENTS.md "Validated Against" line must match this repo's system-constitution.md **Version**:. Tool: tools/check_platform_version.py. Warns until major version drift. |
| 3 | Anti-pattern term scan | Greps changed files for "Reservation", "Branch", "Tenant", "Operator". Warns. (Hard-fail later, once team is bedded in.) |
| 4 | PR auto-tracking issue | If a PR has no Closes/Fixes/Resolves #N link, the workflow auto-creates a tracking Issue, applies a type:* label inferred from the PR title prefix, edits the PR body to insert Closes #M, and (with PROJECT_TOKEN) attaches the Issue to Project #1. Replaced the older strict "must reference an Issue or fail" enforcer (since auto-tracking guarantees the link). |
| 5 | Summary | Posts a checks summary into the PR. |
B. Every merge to main in any repo → (mostly archival)¶
In the new Issue-driven model, merge events are not the trigger for spec status changes. Issues are. Merges still:
- Run the test/build pipeline
- Trigger contract extraction (backend only — see below)
- Optionally post a comment on the linked Issue (
PR #X merged into main)
The legacy post-merge-status.yml flips status from commit messages
as a backstop, but it should be redundant once Issues are in active use.
C. Backend merge to main → extract-contracts.yml (reservation-api-server only)¶
Triggers when any *.py under bookingzone/ or any DocType JSON changes.
reservation-api-server@main
│
▼
.github/scripts/extract_api_contract.py
│
├─→ artifacts/openapi.json (476 whitelisted methods)
└─→ artifacts/doctype-schemas.json (150 BZ* DocTypes)
│
▼
content hash vs contracts/latest/ ──── unchanged? ──→ exit
│ changed
▼
semver bump:
- major: method removed / required field removed
- minor: method added / optional field added
- patch: docstring-only change
│
▼
commit artifacts to bz-platform-context/contracts/v<X.Y.Z>/
update contracts/latest/
bump @bookingzone/api-types to v<X.Y.Z>
│
▼
repository_dispatch ──→ admin-web-app
──→ venuplus-app
──→ customer-web-app
D. Frontend receives api-contract-update event → bump-api-types.yml¶
- Bumps
@bookingzone/api-typesinpackage.json - Regenerates lockfile
- Opens an auto-PR labelled
api-contract automated - Resulting PR's CI runs the frontend's normal build/typecheck/test
- CI passes → forward-compatible. Merge.
- CI fails → a typed call site broke. Treat the failure list as
the work to do. Open a trace spec in
specs/referencing the relevant master, fix, then merge.
E. High-signal repo updates → platform-context-sync-reusable.yml¶
To keep bz-platform-context continuously fresh without mirroring every code
change, source repos can call:
.github/workflows/platform-context-sync-reusable.yml
The caller decides when to sync (typically push to main with path filters
for high-signal files). The reusable workflow then:
- Updates
sync-state/<repo>.jsonwith source SHA/ref, mode, tags, summary - Keeps a rolling history (last 25 events) for traceability
- Reuses one rolling branch (
sync/<repo>) and one open PR per repo
This gives continuous signal freshness with low PR noise. See
docs/PLATFORM-CONTEXT-SYNC.md for the full
policy and caller example.
What is NOT automated (by design)¶
These remain human-driven; AI in Cursor/Claude Code helps but doesn't auto-merge:
| Activity | Why manual |
|---|---|
| Writing the content of specs (problem, user stories, success criteria) | Captures intent — only humans + AI in IDE understand intent |
| Constitution amendments | Architectural decisions need human review and 2 maintainer approvals |
| Glossary additions | Domain naming is high-stakes, slow-changing |
| ADRs | Documenting why requires context machines don't have |
| Moving Issues through Project columns | The team does this in stand-up; bot reflects it |
Deciding In QA → Done |
QA sign-off is a human decision |
| Sprint planning | Selecting next sprint's capacity is a team conversation |
The automation handles the structural plumbing. Humans handle the semantic content. This is the only sustainable split.
Required GitHub setup (one-time)¶
For the automation to work, the following must exist:
1. GitHub App or PAT: bz-platform-bot¶
A bot identity with these scopes/permissions:
- Read on every BookingZone repo
- Write (contents + pull-requests) on
bz-platform-context - Write (workflows + pull-requests) on
admin-web-app,venuplus-app,customer-web-app
Recommended: GitHub App (more granular, auditable). Alternative: a fine-grained PAT on a dedicated bot user.
2. Repo secrets¶
Add the bot token as a secret named PLATFORM_CONTEXT_TOKEN in:
reservation-api-serveradmin-web-appvenuplus-appcustomer-web-app- Any repo that calls
platform-context-sync-reusable.yml
3. Branch protection¶
On bz-platform-context main:
- Required PR review (1 maintainer for spec changes, 2 for constitution changes)
- Required status checks: spec-lint
- No direct pushes (bot included — bot pushes via PR for non-trivial changes)
On consumer repos:
- Required status check:
Spec Discipline / discipline - Required status check: existing build/test workflows
4. Repo registry¶
The list of repos to dispatch to lives in
extract-contracts.yml
under strategy.matrix.repo. Update it when adding a new consumer.
Local development workflow¶
As an engineer working on a repo¶
- Pull main; create a feature branch.
- Either:
- Repo-local feature —
pnpm specify spec(or/speckit-specifyin Cursor) → writespecs/NNN-<slug>/spec.md - Cross-cutting feature — open a PR to
bz-platform-contextaddingspecs/SYS-NNN-<slug>/spec.md(master) + arepo-impact/<repo>.mdfor each impacted repo. Once merged, create trace specs in each repo. - Implement. Push. PR.
- CI runs spec-discipline. Address any failures.
- Merge. Status auto-flips. Move on.
As a platform maintainer (changing the constitution / glossary / ADRs)¶
- PR to
bz-platform-context. - Bump constitution version per the rules in
system-constitution.md. - Add an Amendment Log entry.
- Two approvals required.
- After merge, consumer repos see the version mismatch in their
next CI run; update each repo's
AGENTS.md"Validated Against".
Failure modes and recovery¶
| Failure | Symptom | Recovery |
|---|---|---|
lint_specs.py fails |
PR red on "Spec Discipline / discipline" | Read the error; fix the frontmatter; force-push the branch. |
| Anti-pattern term flagged | Warning in CI logs | Rename in code/docs. (Currently warn-only; will be hard fail.) |
| Large-PR without spec link | PR red | Either link a spec, or add No spec needed: <reason> in PR body. |
| Contract extractor fails | extract-contracts.yml red on backend main |
This blocks frontend bump but not the merge itself. Investigate via job logs; usually a DocType JSON parse error. |
| Repository dispatch fails | Frontend doesn't get a bump PR | Check PLATFORM_CONTEXT_TOKEN scopes. Manually re-run extract-contracts.yml once fixed. |
| Auto-bump PR CI fails | Frontend has typed drift | Treat as work-to-do. Open a trace spec referencing the master that caused the bump. Fix calls. |
| Stale spec status | Spec stays Draft despite work happening |
The PR body didn't link the spec. Edit the PR body, or directly edit specs/NNN/spec.md frontmatter. |
Performance and cost¶
- Reusable workflows mean ~30 lines of YAML per consumer repo, not 300.
- All checks run on
ubuntu-latest, free tier on public repos. Private repos: ~2 minutes per PR. Negligible. - Contract extraction: ~6 seconds for 476 methods + 150 DocTypes
(measured against real
bookingzone/package). - Repository dispatch latency: ~10s end to end.
Migration / rollout (from current state)¶
You are HERE. Rollout sequence (revised for Issue-driven model):
| Phase | Action | Owner | Effort |
|---|---|---|---|
| 0 | Push bz-platform-context to GitHub at aerospace-apps/bz-platform-context |
platform | 15 min |
| 1 | Create the GitHub org Project BookingZone Engineering (UI or tools/setup_project.py); add Sprint iteration field via UI |
platform | 30 min |
| 2 | Create bz-platform-bot identity (GitHub App preferred); add PLATFORM_CONTEXT_TOKEN secret to each repo |
DevOps | 30 min |
| 3 | PR each consumer repo (reservation-api-server, admin-web-app, venuplus-app): adds AGENTS.md, .github/PULL_REQUEST_TEMPLATE.md, .github/ISSUE_TEMPLATE/, .github/workflows/{spec-discipline,issue-lifecycle}.yml |
each repo owner | 1 hr/repo |
| 4 | Merge to main. Automation goes live for new PRs and new Issues. |
each repo owner | — |
| 5 | Update projects: reference in each repo's Issue templates to point to the real Project URL/number |
platform | 10 min |
| 6 | Train the team (30-min walkthrough): how to open Issues, board flow, Project columns, what auto-happens | tech lead | 30 min |
| 7 | PR reservation-api-server adding extract-contracts.yml + extract_api_contract.py. First run publishes v0.1.0 of the API contract |
platform | 1 hr |
| 8 | PR each frontend adding bump-api-types.yml |
each frontend owner | 30 min/repo |
| 9 | Optional: incrementally migrate frontends to actually import from '@bookingzone/api-types', starting with one tRPC procedure |
each frontend | gradual |
| 10 | Optional: Slack/Jira bridge if PMs need it | DevOps | half day |
After Phase 6 the team is on the new model. Phases 7–9 add the API contract layer, which is independent of the Issue lifecycle work.