Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"vergil-marketplace": {
"source": {
"source": "github",
"repo": "vergil-project/vergil-plugin"
"repo": "vergil-project/vergil-claude-plugin"
}
}
},
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,4 @@ jobs:
language: python
container-tag: "3.14"
registry-publish: true
secrets:
APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }}
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
secrets: inherit
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,6 @@ marimo/_static/
marimo/_lsp/
__marimo__/
.worktrees/

# Vergil tooling scratch (PR/session working dir)
.vergil/
42 changes: 32 additions & 10 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,27 @@ on-ramp.
### Structure

```text
~/dev/github/mq-rest-admin-python/ ← sessions ALWAYS start here
<project-root>/ ← sessions ALWAYS start here
.git/
CLAUDE.md, src/, tests/, … ← main worktree (usually `develop`)
.worktrees/ ← container for parallel worktrees
issue-454-adopt-worktree-convention/ ← worktree on feature/454-...
CLAUDE.md, ← main worktree (usually `develop`)
.worktrees/ ← container for parallel worktrees
issue-<N>-<short-slug>/ ← worktree on feature/<N>-<short-slug>
```

### Rules

1. **Sessions always start at the project root.**
`cd ~/dev/github/mq-rest-admin-python && claude` — never from inside
`.worktrees/<name>/`. This keeps the memory-path slug stable and shared.
Never start Claude from inside `.worktrees/<name>/`. This keeps the
memory-path slug stable and shared.
2. **Each parallel agent is assigned exactly one worktree.** The session
prompt names the worktree (see Agent prompt contract below).
- For Read / Edit / Write tools: use the worktree's absolute path.
- For Bash commands that touch files: `cd` into the worktree first,
or use absolute paths.
3. **The main worktree is read-only.** All edits flow through a worktree
on a feature branch — the logical endpoint of the standing
"no direct commits to `develop`" policy.
"no direct commits to develop" policy.
4. **One worktree per issue.** Don't stack in-flight issues. When a
branch lands, remove the worktree before starting the next.
5. **Naming: `issue-<N>-<short-slug>`.** `<N>` is the GitHub issue
Expand All @@ -70,22 +70,44 @@ placeholders):
```text
You are working on issue #<N>: <issue title>.

Your worktree is: /Users/pmoore/dev/github/mq-rest-admin-python/.worktrees/issue-<N>-<slug>/
Your worktree is: <project-root>/.worktrees/issue-<N>-<slug>/
Your branch is: feature/<N>-<slug>

Rules for this session:
- Do all git operations from inside your worktree:
cd <absolute-worktree-path> && git <command>
cd <absolute-worktree-path> && vrg-git <command>
- For Read / Edit / Write tools, use the absolute worktree path.
- For Bash commands that touch files, cd into the worktree first
or use absolute paths.
- Do not edit files at the project root. The main worktree is
read-only — all changes flow through your worktree on your
feature branch.
- When you need to run validation, run it from inside your worktree
(vrg-container-run mounts the current directory).
```

All fields are required.

## Shell command policy

Use `vrg-git` instead of `git` for all git operations. Use `vrg-gh`
instead of `gh` for all GitHub CLI operations. These wrappers enforce
subcommand allowlists, flag deny lists, and credential selection.

Raw `git` and `gh` are denied by the permission model. If a command
is not available through the wrappers, explain the situation to the
human who can run it directly via `! <command>` in the prompt.

## Validation

```bash
vrg-container-run -- vrg-validate
```

This is the **only** validation command. Do not run individual linters,
formatters, or other tools outside of `vrg-validate`. If a tool is not
invoked by `vrg-validate`, it is not part of the validation pipeline.

## Project Overview

`pymqrest` is a Python wrapper for the IBM MQ administrative REST API. The project provides a Python mapping layer for MQ REST API attribute translations and command metadata experiments. The current focus is on attribute mapping and metadata modeling.
Expand Down Expand Up @@ -123,7 +145,7 @@ gates.
### Validation

```bash
vrg-docker-run -- vrg-validate # Full validation (runs in dev container)
vrg-container-run -- vrg-validate # Full validation (runs in dev container)
```
- Lock file verification
- Security audit (pip-audit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def read_group_entries() -> list[GroupEntry]:

def fetch_html(href: str) -> str:
url = f"{IBM_DOCS_BASE}{href}"
context = ssl._create_unverified_context() # noqa: S323, SLF001
context = ssl.create_default_context()
request = Request( # noqa: S310
url,
headers={"User-Agent": "pymqrest-pcf-map/1.0", "Accept": "text/html"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ def extract_command_name(html: str) -> str | None:

def fetch_html(href: str) -> str:
url = f"{IBM_DOCS_BASE}{href}"
context = ssl._create_unverified_context() # noqa: S323, SLF001
context = ssl.create_default_context()
request = urllib.request.Request( # noqa: S310
url,
headers={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def handle_data(self, data: str) -> None:


def fetch_index(url: str) -> str:
context = ssl._create_unverified_context() # noqa: S323, SLF001
context = ssl.create_default_context()
request = Request( # noqa: S310
url,
headers={"User-Agent": "pymqrest-pcf-index/1.0", "Accept": "text/html"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def fetch_html(href: str) -> str:
if cache_path.exists():
return cache_path.read_text(encoding="utf-8", errors="ignore")
url = f"{IBM_DOCS_BASE}{href}"
context = ssl._create_unverified_context() # noqa: S323, SLF001
context = ssl.create_default_context()
request = urllib.request.Request( # noqa: S310
url,
headers={
Expand Down
4 changes: 2 additions & 2 deletions examples/channel_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from __future__ import annotations

from dataclasses import dataclass
from os import getenv
from os import environ, getenv

from pymqrest import MQRESTError, MQRESTSession
from pymqrest.auth import LTPAAuth
Expand Down Expand Up @@ -120,7 +120,7 @@ def main(session: MQRESTSession) -> list[ChannelInfo]:
session = MQRESTSession(
rest_base_url=getenv("MQ_REST_BASE_URL", "https://localhost:9443/ibmmq/rest/v2"),
qmgr_name=getenv("MQ_QMGR_NAME", "QM1"),
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), getenv("MQ_ADMIN_PASSWORD", "mqadmin")),
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), environ["MQ_ADMIN_PASSWORD"]),
verify_tls=False,
)

Expand Down
4 changes: 2 additions & 2 deletions examples/dlq_inspector.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from __future__ import annotations

from dataclasses import dataclass
from os import getenv
from os import environ, getenv

from pymqrest import MQRESTSession
from pymqrest.auth import LTPAAuth
Expand Down Expand Up @@ -147,7 +147,7 @@ def _to_int(value: object) -> int:
session = MQRESTSession(
rest_base_url=getenv("MQ_REST_BASE_URL", "https://localhost:9443/ibmmq/rest/v2"),
qmgr_name=getenv("MQ_QMGR_NAME", "QM1"),
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), getenv("MQ_ADMIN_PASSWORD", "mqadmin")),
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), environ["MQ_ADMIN_PASSWORD"]),
verify_tls=False,
)

Expand Down
6 changes: 3 additions & 3 deletions examples/health_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from __future__ import annotations

from dataclasses import dataclass, field
from os import getenv
from os import environ, getenv

from pymqrest import MQRESTError, MQRESTSession
from pymqrest.auth import LTPAAuth
Expand Down Expand Up @@ -122,7 +122,7 @@ def main(sessions: list[MQRESTSession]) -> list[QMHealthResult]:
MQRESTSession(
rest_base_url=getenv("MQ_REST_BASE_URL", "https://localhost:9443/ibmmq/rest/v2"),
qmgr_name=getenv("MQ_QMGR_NAME", "QM1"),
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), getenv("MQ_ADMIN_PASSWORD", "mqadmin")),
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), environ["MQ_ADMIN_PASSWORD"]),
verify_tls=False,
)
)
Expand All @@ -133,7 +133,7 @@ def main(sessions: list[MQRESTSession]) -> list[QMHealthResult]:
MQRESTSession(
rest_base_url=qm2_url,
qmgr_name="QM2",
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), getenv("MQ_ADMIN_PASSWORD", "mqadmin")),
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), environ["MQ_ADMIN_PASSWORD"]),
verify_tls=False,
)
)
Expand Down
6 changes: 3 additions & 3 deletions examples/provision_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from __future__ import annotations

from dataclasses import dataclass, field
from os import getenv
from os import environ, getenv

from pymqrest import MQRESTError, MQRESTSession
from pymqrest.auth import LTPAAuth
Expand Down Expand Up @@ -289,14 +289,14 @@ def _delete(
qm1_session = MQRESTSession(
rest_base_url=getenv("MQ_REST_BASE_URL", "https://localhost:9443/ibmmq/rest/v2"),
qmgr_name="QM1",
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), getenv("MQ_ADMIN_PASSWORD", "mqadmin")),
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), environ["MQ_ADMIN_PASSWORD"]),
verify_tls=False,
)

qm2_session = MQRESTSession(
rest_base_url=getenv("MQ_REST_BASE_URL_QM2", "https://localhost:9444/ibmmq/rest/v2"),
qmgr_name="QM2",
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), getenv("MQ_ADMIN_PASSWORD", "mqadmin")),
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), environ["MQ_ADMIN_PASSWORD"]),
verify_tls=False,
)

Expand Down
4 changes: 2 additions & 2 deletions examples/queue_depth_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from __future__ import annotations

from dataclasses import dataclass
from os import getenv
from os import environ, getenv

from pymqrest import MQRESTSession
from pymqrest.auth import LTPAAuth
Expand Down Expand Up @@ -126,7 +126,7 @@ def _to_int(value: object) -> int:
session = MQRESTSession(
rest_base_url=getenv("MQ_REST_BASE_URL", "https://localhost:9443/ibmmq/rest/v2"),
qmgr_name=getenv("MQ_QMGR_NAME", "QM1"),
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), getenv("MQ_ADMIN_PASSWORD", "mqadmin")),
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), environ["MQ_ADMIN_PASSWORD"]),
verify_tls=False,
)

Expand Down
4 changes: 2 additions & 2 deletions examples/queue_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from __future__ import annotations

from dataclasses import dataclass
from os import getenv
from os import environ, getenv

from pymqrest import MQRESTError, MQRESTSession
from pymqrest.auth import LTPAAuth
Expand Down Expand Up @@ -139,7 +139,7 @@ def main(session: MQRESTSession) -> None:
session = MQRESTSession(
rest_base_url=getenv("MQ_REST_BASE_URL", "https://localhost:9443/ibmmq/rest/v2"),
qmgr_name=getenv("MQ_QMGR_NAME", "QM1"),
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), getenv("MQ_ADMIN_PASSWORD", "mqadmin")),
credentials=LTPAAuth(getenv("MQ_ADMIN_USER", "mqadmin"), environ["MQ_ADMIN_PASSWORD"]),
verify_tls=False,
)

Expand Down
3 changes: 2 additions & 1 deletion tests/pymqrest/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import dataclasses
from os import getenv
from typing import TYPE_CHECKING

import pytest
Expand All @@ -20,7 +21,7 @@
if TYPE_CHECKING:
from collections.abc import Mapping

TEST_PASSWORD = "secret"
TEST_PASSWORD = getenv("MQ_TEST_PASSWORD", "")
STATUS_OK = 200
STATUS_UNAUTHORIZED = 401

Expand Down
3 changes: 2 additions & 1 deletion tests/pymqrest/test_ensure.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import json
from dataclasses import dataclass
from os import getenv
from typing import TYPE_CHECKING

import pytest
Expand All @@ -15,7 +16,7 @@
if TYPE_CHECKING:
from collections.abc import Mapping, Sequence

TEST_PASSWORD = "pass"
TEST_PASSWORD = getenv("MQ_TEST_PASSWORD", "")
EXPECT_ONE_REQUEST = 1
EXPECT_TWO_REQUESTS = 2

Expand Down
3 changes: 2 additions & 1 deletion tests/pymqrest/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import json
from dataclasses import dataclass
from os import getenv
from typing import TYPE_CHECKING

import pytest
Expand All @@ -28,7 +29,7 @@
REQUEST_EXCEPTION_MESSAGE = "boom"
STATUS_INTERNAL_SERVER_ERROR = 500
STATUS_CREATED = 201
TEST_PASSWORD = "pass"
TEST_PASSWORD = getenv("MQ_TEST_PASSWORD", "")
TEST_DEPTH = 5


Expand Down
3 changes: 2 additions & 1 deletion tests/pymqrest/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import json
import time
from dataclasses import dataclass
from os import getenv
from typing import TYPE_CHECKING

import pytest
Expand All @@ -17,7 +18,7 @@
if TYPE_CHECKING:
from collections.abc import Mapping, Sequence

TEST_PASSWORD = "pass"
TEST_PASSWORD = getenv("MQ_TEST_PASSWORD", "")
EXPECT_ONE_POLL = 1
EXPECT_TWO_POLLS = 2
EXPECT_THREE_POLLS = 3
Expand Down
Loading