Metadata-Version: 2.4
Name: methodology-framework
Version: 0.1.2
Summary: Portable process tooling for agent-driven delivery — scripts, playbooks, templates, and specs.
Author: whiteout59
License-Expression: Apache-2.0
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests<3,>=2.31
Requires-Dist: python-frontmatter<2,>=1.1
Requires-Dist: pyyaml<7,>=6.0
Provides-Extra: dev
Requires-Dist: pytest<9,>=8.0; extra == "dev"
Requires-Dist: requests-mock<2,>=1.12; extra == "dev"
Requires-Dist: ruff==0.15.14; extra == "dev"
Requires-Dist: mypy<2,>=1.10; extra == "dev"
Requires-Dist: types-requests<3,>=2.31; extra == "dev"
Requires-Dist: types-PyYAML<7,>=6.0; extra == "dev"
Requires-Dist: vcrpy<9,>=7.0; extra == "dev"
Dynamic: license-file

# methodology-framework

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

## Installation

```bash
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` -- bootstrap a Jira project with the canonical workflow, custom fields, and automation rules
- `methodology_framework/jira_shapes/` -- canonical Jira shape definitions (YAML, package data)

## Jira Bootstrap

Adopting projects provision their Jira project shape from a single CLI command
instead of manually configuring 30+ admin UI screens.

### Prerequisites

- Python 3.12+
- `pip install methodology-framework` (or `pip install -e .` from source)
- For `--apply` mode: `JIRA_ADMIN_TOKEN` environment variable set to a Jira
  admin API token, and `JIRA_USER_EMAIL` set to the email of the Atlassian
  account that owns that token

### Usage

```bash
# Dry-run (default) — print what would be applied, no side-effects
python -m methodology_framework bootstrap-jira \
  --project-key=MYPROJ \
  --jira-host=myorg.atlassian.net \
  --dry-run

# 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

# Apply — provision the Jira project via admin API
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 \
  --apply

# Apply with --force to skip confirmation prompts
python -m methodology_framework bootstrap-jira \
  --project-key=MYPROJ \
  --jira-host=myorg.atlassian.net \
  --apply --force
```

### 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. |
| `--dry-run` | No | Print what would be applied (default if no mode specified). |
| `--apply` | No | POST/PUT to Jira admin API. Requires `JIRA_ADMIN_TOKEN` env var. |
| `--export <path>` | No | Emit importable config bundle at `<path>`. |
| `--force` | No | Skip confirmation prompts during `--apply`. |

### Environment variables

| Variable | When needed | Description |
|----------|-------------|-------------|
| `JIRA_ADMIN_TOKEN` | `--apply` mode | Jira admin API token. **Never** accepted as a CLI flag. |
| `JIRA_USER_EMAIL` | `--apply` 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. |

### 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 tag
> (`@v0.X.Y`), never `@main`. The framework version must be explicit
> in the caller so updates are deliberate, not silent. This matches the
> version-pinning model per methodology requirements § 4.13.

### Story sync workflow

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

```yaml
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:
    uses: whiteout59/methodology-framework/.github/workflows/sync.yml@v0.1.0
    with:
      project_key: SCRUM
      repo: whiteout59/centralized-pipeline-ui
      story_path_pattern: "docs/stories/{phase1,phase2}/**/*.md"
      cf_story_file: "customfield_10073"
      cf_requirement_ids: "customfield_10141"
      cf_agent_estimate: "customfield_10074"
      mode: ${{ github.event.inputs.mode || 'since-ref' }}
      jira_base_url: "https://myorg.atlassian.net"
      jira_user_email: "devin-sync@myorg.atlassian.net"
    secrets:
      JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
```

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.

### 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:

```yaml
name: Build and register playbook

on:
  push:
    branches: [main]
    paths:
      - "methodology/playbooks/*.body.md"
      - "docs/jira-pickup-config.md"
  workflow_dispatch:

jobs:
  build-and-register:
    uses: whiteout59/methodology-framework/.github/workflows/build-and-register.yml@v0.1.0
    with:
      playbook_body_path: "methodology/playbooks/scrum-router.body.md"
      bindings_path: "docs/jira-pickup-config.md"
    secrets:
      DEVIN_API_TOKEN: ${{ secrets.DEVIN_API_TOKEN }}
```

> **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.

## PyPI Publishing

The package is published to PyPI automatically via OIDC Trusted Publishers
when a SemVer tag is pushed:

```bash
git tag v0.1.0
git push origin v0.1.0
```

The `pypi-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 `methodology-framework`
to the GitHub repo `whiteout59/methodology-framework`, workflow
`pypi-release.yml`, environment `pypi` at
[PyPI Trusted Publishers](https://pypi.org/manage/account/publishing/).

## Release lifecycle

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

### Normal flow

1. A PR bumps the `version` field in `pyproject.toml` (e.g. `"0.1.1"` → `"0.1.2"`).
2. Reviewer approves and merges the PR to `main`.
3. The `auto-tag.yml` workflow detects the `pyproject.toml` change, extracts the
   version, validates it as stable SemVer (`^[0-9]+\.[0-9]+\.[0-9]+$`), and
   creates an annotated tag `v0.1.2`.
4. The `pypi-release.yml` workflow fires on the new tag and publishes the
   package to PyPI via OIDC Trusted Publishers.

**Operator surface = PR review only.** No manual `git tag` step required.

### Manual fallback

If `auto-tag.yml` doesn't fire (workflow disabled, branch protection
blocking the tag push, GitHub Actions outage, etc.), the manual fallback
still works:

```bash
git tag -a v0.1.2 -m "Release v0.1.2"
git push origin v0.1.2
```

`pypi-release.yml` doesn't care how the tag arrived — it fires on any
tag matching `v[0-9]+.[0-9]+.[0-9]+`.

### Pre-release versions

The `auto-tag.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:
   ```bash
   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.

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.

## Development

```bash
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:

```bash
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:

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

## License

Apache-2.0
