Skip to content

Commit a6f9566

Browse files
committed
updated readme
1 parent 37df411 commit a6f9566

7 files changed

Lines changed: 116 additions & 17 deletions

File tree

.github/workflows/phase1-ci-and-release.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
pull_request:
55
push:
66
branches: ["main", "phase1"]
7+
tags: ["v*"]
78
workflow_dispatch:
89
inputs:
910
publish:
@@ -47,7 +48,7 @@ jobs:
4748
publish-predicate-contracts:
4849
runs-on: ubuntu-latest
4950
needs: [quality]
50-
if: github.event_name == 'workflow_dispatch' && inputs.publish == 'true'
51+
if: (github.event_name == 'workflow_dispatch' && inputs.publish == 'true') || startsWith(github.ref, 'refs/tags/v')
5152
steps:
5253
- name: Checkout
5354
uses: actions/checkout@v4
@@ -63,6 +64,10 @@ jobs:
6364
- name: Verify release order
6465
run: python scripts/verify_release_order.py
6566

67+
- name: Validate release tag version
68+
if: startsWith(github.ref, 'refs/tags/v')
69+
run: python scripts/validate_release_tag.py --tag "${GITHUB_REF_NAME}"
70+
6671
- name: Build predicate-contracts
6772
run: python -m build predicate_contracts
6873

@@ -78,7 +83,7 @@ jobs:
7883
publish-predicate-authority:
7984
runs-on: ubuntu-latest
8085
needs: [publish-predicate-contracts]
81-
if: github.event_name == 'workflow_dispatch' && inputs.publish == 'true'
86+
if: (github.event_name == 'workflow_dispatch' && inputs.publish == 'true') || startsWith(github.ref, 'refs/tags/v')
8287
steps:
8388
- name: Checkout
8489
uses: actions/checkout@v4
@@ -94,6 +99,10 @@ jobs:
9499
- name: Verify release order
95100
run: python scripts/verify_release_order.py
96101

102+
- name: Validate release tag version
103+
if: startsWith(github.ref, 'refs/tags/v')
104+
run: python scripts/validate_release_tag.py --tag "${GITHUB_REF_NAME}"
105+
97106
- name: Build predicate-authority
98107
run: python -m build predicate_authority
99108

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
.PHONY: hooks lint test examples verify-release-order build-packages format format-python format-docs lint-docs dev-install
1+
.PHONY: hooks lint test examples verify-release-order build-packages format format-python format-docs lint-docs dev-install tag-release
22

33
dev-install:
44
python -m pip install -e predicate_contracts -e predicate_authority
55

6+
tag-release:
7+
@test -n "$(VERSION)" || (echo "Usage: make tag-release VERSION=X.Y.Z" && exit 1)
8+
python scripts/validate_release_tag.py --tag "v$(VERSION)"
9+
git tag -a "v$(VERSION)" -m "Release v$(VERSION)"
10+
@echo "Created tag v$(VERSION). Push with: git push origin v$(VERSION)"
11+
612
hooks:
713
pre-commit install
814

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
[![License](https://img.shields.io/badge/License-MIT%2FApache--2.0-blue.svg)](LICENSE)
66
[![PyPI - predicate-authority](https://img.shields.io/pypi/v/predicate-authority.svg)](https://pypi.org/project/predicate-authority/)
77
[![PyPI - predicate-contracts](https://img.shields.io/pypi/v/predicate-contracts.svg)](https://pypi.org/project/predicate-contracts/)
8+
[![Release Tag](https://img.shields.io/badge/release-vX.Y.Z-blue)](docs/pypi-release-guide.md)
89

910
`predicate-authority` is a production-grade pre-execution authority layer that binds AI agent identity to deterministic state. It bridges standard IdPs (Entra ID, Okta, OIDC) with runtime verification so every sensitive action is authorized, bounded, and provable.
1011

@@ -69,6 +70,9 @@ make dev-install
6970
# equivalent: python -m pip install -e predicate_contracts -e predicate_authority
7071
```
7172

73+
Release note: publish is supported by pushing a synchronized git tag `vX.Y.Z`
74+
(see `docs/pypi-release-guide.md`).
75+
7276
For shared contracts directly:
7377

7478
```bash

docs/pypi-release-guide.md

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,28 @@ python -m build predicate_authority
4949
- `publish-predicate-contracts`
5050
- `publish-predicate-authority` (runs only after contracts publish succeeds)
5151

52+
## 3b) Publish via git tag (recommended for traceability)
53+
54+
This repository supports tag-triggered publish with a single synchronized tag:
55+
56+
- tag format: `vX.Y.Z`
57+
- both packages must use the same version `X.Y.Z`
58+
59+
When a tag like `v0.2.0` is pushed:
60+
61+
- workflow `phase1-ci-and-release` triggers on the tag,
62+
- release order remains enforced,
63+
- `scripts/validate_release_tag.py` verifies:
64+
- `predicate_contracts/pyproject.toml` version == `predicate_authority/pyproject.toml` version,
65+
- tag version matches both package versions.
66+
67+
Example:
68+
69+
```bash
70+
make tag-release VERSION=0.1.0
71+
git push origin v0.1.0
72+
```
73+
5274
## 4) Verify published artifacts
5375

5476
```bash
@@ -60,23 +82,14 @@ print("ok", predicate_contracts.__name__, predicate_authority.__name__)
6082
PY
6183
```
6284

63-
## 5) Optional: create git tags per package release
64-
65-
Tags are not required for publishing in this repo, but they are recommended for traceability.
85+
## 5) Optional: manual tags (legacy style)
6686

67-
Suggested tag format:
87+
If you need package-specific traceability tags for historical reasons, you can still add:
6888

6989
- `predicate-contracts-vX.Y.Z`
7090
- `predicate-authority-vX.Y.Z`
7191

72-
Example commands (after publish succeeds):
73-
74-
```bash
75-
git tag -a predicate-contracts-v0.1.0 -m "predicate-contracts v0.1.0"
76-
git tag -a predicate-authority-v0.1.0 -m "predicate-authority v0.1.0"
77-
git push origin predicate-contracts-v0.1.0
78-
git push origin predicate-authority-v0.1.0
79-
```
92+
These tags are informational only; automated publish is wired to `vX.Y.Z`.
8093

8194
## 6) Manual fallback publish (if needed)
8295

predicate_authority/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# predicate-authority
22

3-
`predicate-authority` provides pre-execution authorization for AI agent actions.
3+
`predicate-authority` is a deterministic pre-execution authority layer for AI agents.
4+
It binds identity, policy, and runtime evidence so risky actions are authorized
5+
before execution and denied fail-closed when checks do not pass.
6+
7+
Docs: https://www.PredicateSystems.ai/docs
48

59
Core pieces:
610

predicate_authority/pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
55
[project]
66
name = "predicate-authority"
77
version = "0.1.0"
8-
description = "Pre-execution authority enforcement runtime for AI agents."
8+
description = "Deterministic pre-execution authority layer for AI agents."
99
readme = "README.md"
1010
requires-python = ">=3.11"
1111
license = "MIT OR Apache-2.0"
@@ -27,6 +27,9 @@ predicate-authorityd = "predicate_authority.daemon:main"
2727
[project.optional-dependencies]
2828
telemetry = ["opentelemetry-api>=1.24.0"]
2929

30+
[project.urls]
31+
Documentation = "https://www.PredicateSystems.ai/docs"
32+
3033
[tool.setuptools]
3134
packages = ["predicate_authority", "predicate_authority.integrations"]
3235

scripts/validate_release_tag.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from __future__ import annotations
2+
3+
import argparse
4+
import sys
5+
from pathlib import Path
6+
7+
8+
def _read_version(pyproject_path: Path) -> str:
9+
content = pyproject_path.read_text(encoding="utf-8")
10+
for raw_line in content.splitlines():
11+
line = raw_line.strip()
12+
if line.startswith("version = "):
13+
value = line.split("=", maxsplit=1)[1].strip().strip('"')
14+
if value:
15+
return value
16+
raise RuntimeError(f"Unable to read version from {pyproject_path}")
17+
18+
19+
def main() -> int:
20+
parser = argparse.ArgumentParser(
21+
description="Validate git release tag against package versions."
22+
)
23+
parser.add_argument(
24+
"--tag",
25+
required=True,
26+
help="Git tag name, expected format vX.Y.Z",
27+
)
28+
args = parser.parse_args()
29+
tag = args.tag.strip()
30+
if not tag.startswith("v"):
31+
raise SystemExit("Release tag must start with 'v' (example: v0.1.0)")
32+
tag_version = tag[1:]
33+
if tag_version == "":
34+
raise SystemExit("Release tag version cannot be empty")
35+
36+
repo_root = Path(__file__).resolve().parents[1]
37+
contracts_version = _read_version(repo_root / "predicate_contracts" / "pyproject.toml")
38+
authority_version = _read_version(repo_root / "predicate_authority" / "pyproject.toml")
39+
40+
if contracts_version != authority_version:
41+
raise SystemExit(
42+
"Package versions are not in sync: "
43+
f"predicate-contracts={contracts_version}, predicate-authority={authority_version}"
44+
)
45+
if tag_version != contracts_version:
46+
raise SystemExit(
47+
f"Tag version {tag_version} does not match package version {contracts_version}"
48+
)
49+
50+
print(
51+
"release tag validated:",
52+
f"tag={tag}",
53+
f"predicate-contracts={contracts_version}",
54+
f"predicate-authority={authority_version}",
55+
)
56+
return 0
57+
58+
59+
if __name__ == "__main__":
60+
sys.exit(main())

0 commit comments

Comments
 (0)