Skip to main content

Portable process tooling for agent-driven delivery — scripts, playbooks, templates, and specs.

Project description

PyPI version

methodology-framework

Portable process tooling for agent-driven delivery -- scripts, playbooks, templates, and specs.

Installation

pip install FlywheelIC
# or, from a source checkout:
pip install -e .

Package contents

  • methodology_framework.sync_stories_to_jira -- one-way repo-to-Jira story sync
  • methodology_framework.build_playbook -- build a concrete playbook from a parameterized body + bindings
  • methodology_framework.register_playbook_with_devin -- register a rendered playbook with Devin's API
  • methodology_framework/playbooks/ -- parameterized playbook bodies (package data)
  • methodology_framework/templates/ -- story and process templates (package data)
  • methodology_framework/specs/ -- format specs (package data)
  • methodology_framework.bootstrap_jira -- verify a Jira project matches the canonical shape (read-only spec-compliance checker)
  • methodology_framework/jira_shapes/ -- canonical Jira shape definitions (YAML, package data)

Jira Bootstrap (Verify Mode)

The CLI checks whether a Jira project matches the methodology-framework's canonical shape and reports gaps. It does not provision — operators apply gaps via the Jira UI. This read-only design lets the tool run in CI as a gate.

v0.2.0 breaking change: --apply and --dry-run were removed. The CLI is now a spec-compliance verifier. See operator-verify-runbook.md for the operator workflow.

Prerequisites

  • Python 3.12+
  • pip install FlywheelIC (or pip install -e . from source)
  • For --verify mode: JIRA_ADMIN_TOKEN environment variable set to a Jira API token (admin scope is not required; a regular user token with read access is sufficient), and JIRA_USER_EMAIL set to the email of the Atlassian account that owns the token

Usage

# Verify — query Jira and report spec-compliance gaps (read-only)
export JIRA_ADMIN_TOKEN="<your-token>"
export JIRA_USER_EMAIL="<your-email>"
python -m methodology_framework bootstrap-jira \
  --project-key=MYPROJ \
  --jira-host=myorg.atlassian.net \
  --verify

# JSON output (stable schema — safe for CI parsing)
python -m methodology_framework bootstrap-jira \
  --project-key=MYPROJ \
  --jira-host=myorg.atlassian.net \
  --verify --output=json

# Markdown output
python -m methodology_framework bootstrap-jira \
  --project-key=MYPROJ \
  --jira-host=myorg.atlassian.net \
  --verify --output=markdown

# Export — emit an importable config bundle (YAML); no API calls
python -m methodology_framework bootstrap-jira \
  --project-key=MYPROJ \
  --jira-host=myorg.atlassian.net \
  --export /tmp/jira-bundle.yaml

Exit codes

Code Meaning
0 All spec items verified OK
1 One or more items missing (gaps found)
2 One or more items unverifiable (API limitation; operator must inspect manually)
3 Error (connectivity, auth, or runtime failure)

Flags

Flag Required Description
--project-key Yes Jira project key (e.g. SCRUM). Substituted for {{PROJECT_KEY}} in shape defs.
--jira-host Yes Jira Cloud host (e.g. myorg.atlassian.net). No https:// prefix.
--verify No Query Jira and report spec-compliance gaps (read-only).
--export <path> No Emit importable config bundle at <path>. No API calls.
--output No Output format for --verify: text (default), json, markdown.

Environment variables

Variable When needed Description
JIRA_ADMIN_TOKEN --verify mode Jira API token. Admin scope is not required; a regular user token with read access is sufficient. Never accepted as a CLI flag.
JIRA_USER_EMAIL --verify mode Email of the Atlassian account that owns the API token. Used with the token for HTTP Basic auth. Never accepted as a CLI flag.

JSON output schema (v0.2.0)

{
  "spec_version": "0.2.0",
  "project_key": "METH",
  "verified_at": "2025-05-31T00:00:00Z",
  "items": [
    {"resource_type": "status", "name": "To Do", "result": "ok", "details": null},
    {"resource_type": "custom_field", "name": "Story File", "result": "missing", "details": "Operator must create via Settings → Custom fields → Create"}
  ],
  "summary": {"total": 14, "ok": 12, "missing": 1, "unverifiable": 1},
  "exit_code": 1
}

Any breaking change to this schema requires a MAJOR version bump.

Shape definitions

The three canonical shape files shipped as package data:

  • jira_shapes/workflow.yaml — workflow statuses (To Do, Ready for AI agent, In Progress, In Review, Waiting, Blocked, Done, Won't do) and transitions with actor permissions
  • jira_shapes/custom_fields.yaml — Story File (URL), Requirement IDs (labels), Agent Estimate (number)
  • jira_shapes/automation_rules.yaml — PR-title-key auto-transition, dependency resolution, BLOCKED protection

For full methodology context see the methodology requirements doc § 4.13.3 ("Jira Bootstrap CLI").

Adopter Integration

Adopting projects consume the methodology framework's CI pipelines via reusable GitHub Actions workflows. Instead of copying workflow YAML into each repo, adopters write a thin caller that references the framework's workflows by SemVer tag.

Version pinning requirement: adopters MUST pin to a specific workflow tag (@v0.X.Y), never @main. The reusable workflow at that tag owns the matching PyPI package install, so normal adopters update only the uses: ref. This keeps workflow and package updates deliberate, not silent, and matches the version-pinning model per methodology requirements § 4.13.

For adopter and intern repos, the normal contract is:

uses: invoice-cloud/Flywheel/.github/workflows/sync.yml@v0.4.6
with:
  install_from_local: false

install_from_local: true is reserved for framework-internal validation where this repository dogfoods unreleased HEAD code. Adopter and intern repos should normally use install_from_local: false or omit it and accept the default. The v0.4.6 reusable workflows install FlywheelIC==0.4.6 from PyPI by default. Reusable workflow install paths should not use unpinned pip install FlywheelIC.

Advanced operators may override flywheel_version, but routine adopter and intern release adoption should not need it. The expected release motion is: update uses: invoice-cloud/Flywheel/.github/workflows/<workflow>.yml@vX.Y.Z and let that reusable workflow install its matching FlywheelIC==X.Y.Z default.

The same single-knob contract applies to all adopter-facing reusable workflows that execute Flywheel code:

  • sync.yml
  • populate-story-acus.yml
  • build-and-register.yml

Each reusable workflow exposes flywheel_version only as an advanced optional override. Normal adopter and intern callers should pin the reusable workflow ref and should not pass flywheel_version.

For the local wrapper model, one-command wrapper upgrades, stale-ref guard, and intern controlled-test checklist, see docs/flywheel-wrapper-automation.md.

Required inputs

Input Required Default Description
project-key Yes Jira project key (e.g. METH, SCRUM). No default — sync errors if absent.
jira-auth-mode No oauth Jira auth mode. OAuth client credentials are the standard CI path. Set basic only for legacy API-token callers.
jira-cloud-id Yes for OAuth Atlassian cloud ID used by OAuth gateway requests. Operators/repo factory provision this; interns should not manage it.
jira-base-url Yes for Basic Jira Cloud base URL (e.g. https://icpipeline.atlassian.net). Optional for OAuth gateway mode.
jira-user-email Yes for Basic Jira service account email for legacy HTTP Basic auth. Not required for OAuth.
stories-dir No docs/stories Relative path to the stories directory.
phase-regex No ^phase\d+$ Regex for phase directory matching. Empty string ("") disables phase labeling entirely.
repo-url No (auto) Override repo URL. Normally derived from GITHUB_SERVER_URL + GITHUB_REPOSITORY (GHA-native).
flywheel_version No 0.4.6 Advanced override for the FlywheelIC PyPI package version installed when install_from_local is false. Omit in normal adopter/intern callers; the reusable workflow ref owns the default.
install_from_local No false Use false in adopter/intern repos. Use true only for framework-internal HEAD validation.

Per-tenant discovery

Sync discovers all tenant-specific Jira IDs at runtime via the REST API, using canonical names as lookup keys. The names come from the framework's shape definitions (jira_shapes/*.yaml) — the same spec the verifier (bootstrap-jira --verify) enforces.

Custom fields (discovered via GET /rest/api/3/field):

Name Purpose
Requirement IDs Multi-value field for REQ-id traceability
Story File URL to the canonical story .md file in the repo
Agent Estimate Numeric estimate in agent-hours

Statuses (discovered via GET /rest/api/3/project/{key}/statuses):

Name Category
To Do TODO
Ready for AI agent TODO
In Progress IN_PROGRESS
In Review IN_PROGRESS
Waiting IN_PROGRESS
Blocked IN_PROGRESS
Done DONE
Won't do DONE

Transitions: Sourced from the workflow spec (jira_shapes/workflow.yaml).

Link types (discovered via GET /rest/api/3/issueLinkType): Blocks (inward: "is blocked by").

Issue types (discovered via GET /rest/api/3/issuetype): Story, Sub-task.

If any required name is missing from the target project, sync fails fast:

ERROR: Required custom field 'Story File' not found in Jira project METH;
run 'methodology-framework bootstrap-jira --verify --project-key=METH' to identify the gap.

Story sync workflow

Syncs story .md files from the adopter's repo to Jira. Create .github/workflows/sync-stories.yml in the adopter repo:

name: Sync stories to Jira

on:
  push:
    branches: [main]
    paths:
      - "docs/stories/**/*.md"
  workflow_dispatch:
    inputs:
      mode:
        description: "Sync mode"
        required: true
        default: "since-ref"
        type: choice
        options:
          - since-ref
          - all

jobs:
  sync:
    permissions:
      contents: write
      pull-requests: read
    uses: invoice-cloud/Flywheel/.github/workflows/sync.yml@v0.4.6
    with:
      project_key: MYPROJ
      mode: ${{ github.event.inputs.mode || 'since-ref' }}
      jira_auth_mode: oauth
      jira_cloud_id: ${{ vars.JIRA_CLOUD_ID }}
      jira_base_url: ${{ vars.JIRA_BASE_URL }}
      install_from_local: false
      # stories_dir: "docs/stories"    # default
      # phase_regex: "^phase\\d+$"     # default; set "" to disable
    secrets:
      JIRA_OAUTH_CLIENT_ID: ${{ secrets.JIRA_OAUTH_CLIENT_ID }}
      JIRA_OAUTH_CLIENT_SECRET: ${{ secrets.JIRA_OAUTH_CLIENT_SECRET }}

The mode input defaults to since-ref for routine push-triggered runs (preserves the 250-story scale guard). Pass mode: all explicitly for nightly or workflow_dispatch full-corpus syncs.

Secrets forwarding: the caller's secrets: block must explicitly map each secret the reusable workflow declares. Secrets are NOT inherited by default in reusable workflows. Using secrets: inherit works but is brittle — prefer explicit mapping.

Legacy Basic auth remains available for existing adopters by setting jira_auth_mode: basic, passing jira_user_email, and forwarding JIRA_API_TOKEN. New CI/reusable workflow callers should use OAuth client credentials provisioned by operators or the repo factory.

Required caller permissions

The reusable sync workflow declares permissions: contents: write internally (for jira_key + agent_acus writeback commits). GitHub Actions requires the caller to grant at least the same permissions at the job level — otherwise the workflow fails at the uses: resolution step with:

The workflow is requesting 'contents: write', but is only allowed 'contents: read'.

Add a job-level permissions: block to your caller:

jobs:
  sync:
    permissions:
      contents: write
      pull-requests: read
    uses: invoice-cloud/Flywheel/.github/workflows/sync.yml@v0.4.6
    # ... with: / secrets: as above

Job-scoped, not workflow-scoped. Keep permissions: on the job, not the top-level on: — if future jobs are added to the same file, they should not inherit write access they don't need.

Playbook build + register workflow

Builds a concrete playbook from a parameterized body + bindings file, then registers it with Devin's API. Create .github/workflows/build-and-register.yml in the adopter repo:

name: Build and register playbook

on:
  push:
    branches: [main]
    paths:
      - "methodology/playbooks/*.body.md"
      - ".flywheel/playbook-bindings.yml"
  workflow_dispatch:

jobs:
  build-and-register:
    uses: invoice-cloud/Flywheel/.github/workflows/build-and-register.yml@v0.4.6
    with:
      playbook_body_path: "methodology/playbooks/router-base.body.md"
      bindings_path: ".flywheel/playbook-bindings.yml"
      devin-playbook-scope: org
      devin-org-id: ${{ vars.DEVIN_ORG_ID }}
      install_from_local: false
    secrets:
      DEVIN_API_TOKEN: ${{ secrets.DEVIN_API_TOKEN }}

Devin org playbooks and enterprise playbooks are separate stores. The reusable workflow defaults to devin-playbook-scope: org, which requires the DEVIN_API_TOKEN secret and DEVIN_ORG_ID variable. To register account-level enterprise playbooks, set the GitHub variable DEVIN_PLAYBOOK_SCOPE=enterprise or pass devin-playbook-scope: enterprise, and provide the DEVIN_ENTERPRISE_API_TOKEN secret instead. The enterprise service user needs an account-level custom role with ManageAccountPlaybooks; do not grant broader admin permissions just for playbook registration. Enterprise playbook IDs are returned by Devin with the playbook- prefix.

Dedicated Devin Enterprise customers should also set DEVIN_API_ROOT to their deployment API host, for example https://api.<company>.devinenterprise.com. InvoiceCloud uses DEVIN_API_ROOT=https://api.invoicecloud.devinenterprise.com. If DEVIN_API_ROOT is unset or empty, Flywheel defaults to https://api.devin.ai.

Nesting limit: GitHub allows reusable workflow nesting up to 4 levels deep. If your repo wraps these workflows in another caller layer, verify the total nesting depth stays within limits.

How this repo evolves

The methodology-framework repo itself uses the same sync workflow it ships to adopters. Framework-side evolution stories (new specs, CLI features, workflow improvements) are drafted as .md files under docs/stories/ and synced to the METH Jira project via .github/workflows/sync-stories.yml. Adopter-side stories (product-specific work) file in each adopter's own Jira project (e.g. SCRUM for centralized-pipeline-ui).

PyPI Publishing

The package is published to PyPI automatically via OIDC Trusted Publishing when pyproject.toml changes on main.

The release.yml workflow builds and publishes using pypa/gh-action-pypi-publish in OIDC mode -- no PYPI_API_TOKEN secret is needed. The job uses the pypi GitHub environment for protection rules.

Operator setup (one-time): bind the PyPI project FlywheelIC to the GitHub repo invoice-cloud/Flywheel, workflow release.yml, environment pypi at PyPI Trusted Publishers.

Release lifecycle

Version bumps in pyproject.toml drive the entire release cycle automatically. The operator's only decision surface is PR review.

Primary flow

  1. A PR bumps the version field in pyproject.toml (e.g. "0.2.0""0.2.1").
  2. The same release PR keeps each adopter-facing reusable workflow's flywheel_version default aligned to that package version.
  3. Reviewer approves and merges the PR to main.
  4. release.yml fires on the pyproject.toml change, extracts the version, validates it as stable SemVer (^[0-9]+\.[0-9]+\.[0-9]+$), builds the package, creates an annotated tag such as v0.2.1, pushes the tag, and publishes to PyPI via OIDC Trusted Publishing.

Operator surface = PR review only. No manual git tag step, no manual publish trigger for the normal path.

Fallback (operator-pushed tag)

If the pyproject.toml push trigger does not fire, an operator can run release.yml manually with the stable SemVer version input.

Pre-release versions

The release.yml workflow only tags stable SemVer versions. Versions containing -rc, -beta, -alpha, .dev, or .pre suffixes (or any string not matching ^[0-9]+\.[0-9]+\.[0-9]+$) are logged and skipped. Pre-release publishing is out of scope; if needed, a separate workflow would handle pre-release tag patterns.

Cost tracking via agent_acus

The methodology's post-execution notes include an agent_acus field that records per-story compute cost. Since agents cannot self-report ACU consumption mid-session, a post-merge populator workflow backfills the value from the Devin API after the session completes.

Adopter wiring (3 steps):

  1. Copy the template caller into your repo:
    cp "$(python -c "import methodology_framework; import pathlib; \
      print(pathlib.Path(methodology_framework.__file__).parent / \
      'templates/github_workflows/populate-story-acus-caller.yml')")" \
      .github/workflows/populate-story-acus.yml
    
  2. Set the DEVIN_API_TOKEN repo secret (Settings > Secrets and variables > Actions > New repository secret).
  3. Pin the framework version in the caller's uses: line, for example uses: invoice-cloud/Flywheel/.github/workflows/populate-story-acus.yml@v0.4.6. Do not pass flywheel_version for normal adopter/intern repos; the reusable workflow owns the matching FlywheelIC==0.4.6 install.

The caller fires on merged PRs that touch story files, invokes the reusable populate-story-acus.yml workflow, and opens a follow-on PR with the populated ACU values.

Contributing

Framework evolution stories — new specs, CLI features, workflow improvements — are filed as Markdown story files under docs/stories/. See the directory README for the filing convention (slug-named directories, frontmatter shape, depends_on hygiene). Stories sync to the METH Jira project via the same sync workflow the framework ships to adopters.

For the canonical story template and format spec, see whiteout59/centralized-pipeline-ui/methodology/templates/story.md and whiteout59/centralized-pipeline-ui/methodology/devin-story-format.md.

Jira Story Sync Auth

Story sync supports JIRA_AUTH_MODE=basic and JIRA_AUTH_MODE=oauth.

OAuth mode is the default for CI and reusable workflows. It requires JIRA_CLOUD_ID plus both JIRA_OAUTH_CLIENT_ID and JIRA_OAUTH_CLIENT_SECRET for client-credentials token exchange via https://api.atlassian.com/oauth/token. OAuth Jira REST calls use the Atlassian API gateway base URL https://api.atlassian.com/ex/jira/{JIRA_CLOUD_ID}.

JIRA_OAUTH_ACCESS_TOKEN is supported only as an optional manual/debug override. Operators or the repo factory provision the required Jira OAuth variable and secrets; intern/adopter repos should not manage these values directly.

Basic mode is legacy/backwards-compatible. Existing adopters may set JIRA_AUTH_MODE=basic or workflow input jira_auth_mode: basic, then provide JIRA_BASE_URL, JIRA_USER_EMAIL, and JIRA_API_TOKEN.

Development

pip install -e ".[dev]"
pytest tests/ -v
ruff check src/methodology_framework
ruff format --check src/methodology_framework
mypy --strict src/methodology_framework

Shape-def expansion (v0.1.2+)

workflow.yaml now includes a statusCategory field on each status (TODO, IN_PROGRESS, or DONE). The --apply handler validates this field at load time and exits with a clear error if any status is missing it.

custom_fields.yaml uses canonical shorthand types (text, string, multi-value, numeric) that are mapped to fully-qualified Jira identifiers at apply time. Fields may also specify a fully-qualified type directly (any value containing : is passed through unchanged).

Recording new VCR cassettes

Integration tests under tests/integration/ replay pre-recorded VCR cassettes in CI (no network access required). To record fresh cassettes against a real Jira instance:

export JIRA_USER_EMAIL="<your-email>"
export JIRA_ADMIN_TOKEN="<your-api-token>"
PYTEST_VCR_MODE=record pytest tests/integration/ -v

Cassettes are stored at tests/fixtures/vcr/*.yaml. The VCR config in tests/integration/conftest.py automatically strips Authorization headers from recorded interactions. Never commit a cassette with a real token in any header.

After recording, verify no credentials leaked:

grep -i 'authorization' tests/fixtures/vcr/*.yaml
# Expected: no output

License

Apache-2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flywheelic-0.4.13.tar.gz (147.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

flywheelic-0.4.13-py3-none-any.whl (112.2 kB view details)

Uploaded Python 3

File details

Details for the file flywheelic-0.4.13.tar.gz.

File metadata

  • Download URL: flywheelic-0.4.13.tar.gz
  • Upload date:
  • Size: 147.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for flywheelic-0.4.13.tar.gz
Algorithm Hash digest
SHA256 800992a4a39039ac3ce00b95aaf7a0553d6e4d86d2634903d2cce1137f59d0be
MD5 5c63526c642e4aa35f8b8dd97aadc764
BLAKE2b-256 5dbe4e89d2184c3e14c9ff0dc3074f75b22cfc6e96703a20aab29fd07984baad

See more details on using hashes here.

Provenance

The following attestation bundles were made for flywheelic-0.4.13.tar.gz:

Publisher: release.yml on invoice-cloud/Flywheel

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file flywheelic-0.4.13-py3-none-any.whl.

File metadata

  • Download URL: flywheelic-0.4.13-py3-none-any.whl
  • Upload date:
  • Size: 112.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for flywheelic-0.4.13-py3-none-any.whl
Algorithm Hash digest
SHA256 ab2e8195f96ef89f1355f3d247c4ab6e83fbf4c8f27551e0235fb705a82dc1ef
MD5 17a475d4ece717e98fe4dcc76e63ec9d
BLAKE2b-256 764c4ba49614c0ae90a3d8b3ce7d13d7aed908f188bc1c14ccee4604277978c4

See more details on using hashes here.

Provenance

The following attestation bundles were made for flywheelic-0.4.13-py3-none-any.whl:

Publisher: release.yml on invoice-cloud/Flywheel

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page