policyengine-observability 0.2.1__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- policyengine_observability-0.3.0/.github/copilot-instructions.md +7 -0
- policyengine_observability-0.3.0/AGENTS.md +23 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/CHANGELOG.md +11 -0
- policyengine_observability-0.3.0/CLAUDE.md +21 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/PKG-INFO +1 -1
- policyengine_observability-0.3.0/docs/engineering/skills/README.md +16 -0
- policyengine_observability-0.3.0/docs/engineering/skills/github-prs.md +46 -0
- policyengine_observability-0.3.0/docs/engineering/skills/repository-guidance.md +80 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/policyengine_observability/runtime.py +6 -1
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/pyproject.toml +1 -1
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/tests/test_runtime.py +72 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/uv.lock +1 -1
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.github/bump_version.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.github/check-changelog.sh +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.github/fetch_version.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.github/get-changelog-diff.sh +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.github/publish-git-tag.sh +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.github/workflows/pr.yml +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.github/workflows/push.yml +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.gitignore +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/README.md +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/policyengine_observability/__init__.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/policyengine_observability/adapters/__init__.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/policyengine_observability/adapters/fastapi.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/policyengine_observability/adapters/flask.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/policyengine_observability/config.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/policyengine_observability/context.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/policyengine_observability/integrations/__init__.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/policyengine_observability/integrations/httpx.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/policyengine_observability/logging.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/policyengine_observability/segments.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/tests/test_fastapi_adapter.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/tests/test_flask_adapter.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/tests/test_public_api.py +0 -0
- {policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/tests/test_release_scripts.py +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Copilot Instructions
|
|
2
|
+
|
|
3
|
+
Repository-wide AI-facing engineering guidance lives in `AGENTS.md`.
|
|
4
|
+
Canonical skills live under `docs/engineering/skills/`.
|
|
5
|
+
|
|
6
|
+
Use those files as the source of truth. Keep this adapter thin and avoid
|
|
7
|
+
duplicating detailed testing, CI, formatting, release, or architecture rules.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Agent Instructions
|
|
2
|
+
|
|
3
|
+
These instructions apply repository-wide.
|
|
4
|
+
|
|
5
|
+
## Skills System
|
|
6
|
+
|
|
7
|
+
Canonical AI-facing engineering skills live under `docs/engineering/skills/`.
|
|
8
|
+
Use those files as the source of truth across Codex, Claude, Copilot, and other
|
|
9
|
+
AI tools.
|
|
10
|
+
|
|
11
|
+
Before opening, replacing, or sharing any pull request, read
|
|
12
|
+
`docs/engineering/skills/github-prs.md`.
|
|
13
|
+
|
|
14
|
+
Before making or reviewing repository-wide API, testing, documentation, release,
|
|
15
|
+
or package-boundary changes, read
|
|
16
|
+
`docs/engineering/skills/repository-guidance.md`.
|
|
17
|
+
|
|
18
|
+
## Repository Boundaries
|
|
19
|
+
|
|
20
|
+
`policyengine-observability` is the shared PolicyEngine observability runtime.
|
|
21
|
+
Keep framework-specific behavior in adapters or integrations, keep the core
|
|
22
|
+
runtime usable outside HTTP requests, and keep observability failures from
|
|
23
|
+
breaking application code.
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
## [0.3.0] - 2026-06-23
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- Add model-agnostic AI harness instructions and canonical engineering skills.
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Preserve internal dispatch segment timings on the parent worker operation log.
|
|
10
|
+
|
|
11
|
+
|
|
1
12
|
## [0.2.1] - 2026-06-22
|
|
2
13
|
|
|
3
14
|
### Changed
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Claude Instructions
|
|
2
|
+
|
|
3
|
+
These instructions apply repository-wide.
|
|
4
|
+
|
|
5
|
+
## Canonical Guidance
|
|
6
|
+
|
|
7
|
+
Repository-wide AI-facing engineering guidance lives in `AGENTS.md`.
|
|
8
|
+
Canonical skills live under `docs/engineering/skills/`.
|
|
9
|
+
|
|
10
|
+
Use those files as the source of truth. This file is a Claude adapter and should
|
|
11
|
+
stay thin; do not duplicate detailed testing, CI, formatting, release, or
|
|
12
|
+
architecture rules here.
|
|
13
|
+
|
|
14
|
+
## Required Skill Lookup
|
|
15
|
+
|
|
16
|
+
Before opening, replacing, or sharing a PR, read
|
|
17
|
+
`docs/engineering/skills/github-prs.md`.
|
|
18
|
+
|
|
19
|
+
Before making or reviewing repository-wide API, testing, documentation, release,
|
|
20
|
+
or package-boundary changes, read
|
|
21
|
+
`docs/engineering/skills/repository-guidance.md`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: policyengine-observability
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Shared PolicyEngine observability runtime for logs, timings, metrics, and OpenTelemetry.
|
|
5
5
|
Author-email: PolicyEngine <hello@policyengine.org>
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Engineering Skills
|
|
2
|
+
|
|
3
|
+
This directory is the canonical source for AI-facing engineering rules.
|
|
4
|
+
|
|
5
|
+
Tool-specific instruction files such as `AGENTS.md`, `CLAUDE.md`, and
|
|
6
|
+
`.github/copilot-instructions.md` should point here instead of duplicating
|
|
7
|
+
implementation-specific guidance. When a rule changes, update the skill here
|
|
8
|
+
first, then keep adapters thin.
|
|
9
|
+
|
|
10
|
+
Current skills:
|
|
11
|
+
|
|
12
|
+
- `github-prs.md`: same-repository PR workflow, required pre-PR checks,
|
|
13
|
+
changelog-fragment requirements, final formatter/linter requirements, PR head
|
|
14
|
+
verification, and title conventions.
|
|
15
|
+
- `repository-guidance.md`: package structure, commands, runtime boundaries,
|
|
16
|
+
test expectations, release expectations, and repo-specific anti-patterns.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# GitHub PRs
|
|
2
|
+
|
|
3
|
+
These rules apply to every developer and AI agent opening pull requests in this
|
|
4
|
+
repository.
|
|
5
|
+
|
|
6
|
+
## Same-Repository PRs
|
|
7
|
+
|
|
8
|
+
Open PRs from branches in `PolicyEngine/policyengine-observability`, not from
|
|
9
|
+
personal forks. Fork PRs are more likely to miss repository secrets and can
|
|
10
|
+
produce different CI behavior from branches in the canonical repository.
|
|
11
|
+
|
|
12
|
+
Before creating or sharing a PR:
|
|
13
|
+
|
|
14
|
+
1. Confirm the canonical repository is reachable:
|
|
15
|
+
`gh repo view PolicyEngine/policyengine-observability --json nameWithOwner`.
|
|
16
|
+
2. Open a GitHub issue for the work, or verify that an appropriate issue
|
|
17
|
+
already exists.
|
|
18
|
+
3. Put `Fixes #ISSUE_NUMBER` as the first line of the PR description, using the
|
|
19
|
+
issue number from the issue created or found in the previous step.
|
|
20
|
+
4. Add a Towncrier changelog fragment under `changelog.d/` using the issue
|
|
21
|
+
number or a clear slug and the appropriate configured type, for example
|
|
22
|
+
`changelog.d/ISSUE_NUMBER.fixed.md`.
|
|
23
|
+
5. Before the final commit in the PR, run the formatter and linter:
|
|
24
|
+
`uv run --extra dev ruff format .` and
|
|
25
|
+
`uv run --extra dev ruff check .`. If formatting changes files, review and
|
|
26
|
+
stage those changes before committing.
|
|
27
|
+
6. Run tests with coverage:
|
|
28
|
+
`uv run --extra dev --extra all coverage run -m pytest` and
|
|
29
|
+
`uv run --extra dev --extra all coverage report`.
|
|
30
|
+
7. Push the current branch to the canonical repository:
|
|
31
|
+
`git push origin HEAD`.
|
|
32
|
+
8. Create the PR as a draft from that same repository:
|
|
33
|
+
`gh pr create --draft --repo PolicyEngine/policyengine-observability --head "$(git branch --show-current)" --base main`.
|
|
34
|
+
9. Verify the PR is draft and the head repository is canonical:
|
|
35
|
+
`gh pr view <PR> --repo PolicyEngine/policyengine-observability --json isDraft,headRepositoryOwner,headRepository`.
|
|
36
|
+
10. Before sharing the PR, verify CI has been checked:
|
|
37
|
+
`gh pr checks <PR> --repo PolicyEngine/policyengine-observability`.
|
|
38
|
+
|
|
39
|
+
If you cannot push to the canonical repository, stop and ask for access. Do not
|
|
40
|
+
create a fork PR as a fallback. If you accidentally create one, close it and
|
|
41
|
+
replace it with a same-repository draft PR.
|
|
42
|
+
|
|
43
|
+
## PR Title
|
|
44
|
+
|
|
45
|
+
Do not add `[codex]`, `[claude]`, `[copilot]`, or other agent labels to PR
|
|
46
|
+
titles. Use a plain descriptive title.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Repository Guidance
|
|
2
|
+
|
|
3
|
+
Use this skill when making or reviewing repository-wide API, testing,
|
|
4
|
+
documentation, release, or package-boundary changes.
|
|
5
|
+
|
|
6
|
+
## Commands
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
uv sync --extra dev --extra all
|
|
10
|
+
uv run --extra dev ruff format .
|
|
11
|
+
uv run --extra dev ruff format --check .
|
|
12
|
+
uv run --extra dev ruff check .
|
|
13
|
+
uv run --extra dev --extra all coverage run -m pytest
|
|
14
|
+
uv run --extra dev --extra all coverage report
|
|
15
|
+
uv build
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
To check for a changelog fragment locally:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
uv run --extra dev towncrier check --compare-with origin/main
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## What Lives Here
|
|
25
|
+
|
|
26
|
+
- `policyengine_observability/config.py` resolves environment-driven runtime
|
|
27
|
+
configuration.
|
|
28
|
+
- `policyengine_observability/context.py` defines request and operation log
|
|
29
|
+
payload structures.
|
|
30
|
+
- `policyengine_observability/runtime.py` owns context management, segments,
|
|
31
|
+
structured logs, metrics, traces, events, and fail-open behavior.
|
|
32
|
+
- `policyengine_observability/adapters/` contains framework adapters such as
|
|
33
|
+
Flask and FastAPI.
|
|
34
|
+
- `policyengine_observability/integrations/` contains optional integrations
|
|
35
|
+
such as HTTP client instrumentation.
|
|
36
|
+
- `.github/` contains changelog, versioning, tagging, and PyPI publication
|
|
37
|
+
automation.
|
|
38
|
+
- `tests/` should cover runtime behavior, framework adapters, public exports,
|
|
39
|
+
and release scripts.
|
|
40
|
+
|
|
41
|
+
## Design Boundaries
|
|
42
|
+
|
|
43
|
+
- Keep the core runtime framework-agnostic. HTTP frameworks belong in adapters.
|
|
44
|
+
- Keep the context-manager API usable from HTTP requests, worker functions,
|
|
45
|
+
CLI scripts, and tests.
|
|
46
|
+
- Keep OpenTelemetry optional and lazily imported. Timing and structured
|
|
47
|
+
logging must work without an OTel backend.
|
|
48
|
+
- Observability failures must fail open: record an internal observability error
|
|
49
|
+
when practical, but do not break the application operation being observed.
|
|
50
|
+
- Preserve structured log schemas. Make additive changes when possible; bump
|
|
51
|
+
schema versions for breaking payload changes.
|
|
52
|
+
- Keep metric attributes bounded and low-cardinality. Do not put raw paths,
|
|
53
|
+
full URLs, request bodies, or unbounded user-provided values into metric
|
|
54
|
+
labels.
|
|
55
|
+
- Keep segment names stable. Prefer registered segment enums in consuming
|
|
56
|
+
applications, while preserving safe string fallback behavior.
|
|
57
|
+
|
|
58
|
+
## Testing
|
|
59
|
+
|
|
60
|
+
Add focused tests for runtime context behavior and failure paths whenever
|
|
61
|
+
changing `runtime.py`. Adapter changes should include framework-level tests that
|
|
62
|
+
exercise request setup, response headers, error paths, and teardown behavior.
|
|
63
|
+
|
|
64
|
+
Release automation changes should include tests for the helper scripts when the
|
|
65
|
+
logic is non-trivial.
|
|
66
|
+
|
|
67
|
+
## Release Expectations
|
|
68
|
+
|
|
69
|
+
Every behavior change should include a Towncrier fragment in `changelog.d/`.
|
|
70
|
+
The push workflow builds the changelog, bumps the package version, tags the
|
|
71
|
+
release, and publishes to PyPI through trusted publishing.
|
|
72
|
+
|
|
73
|
+
## Anti-Patterns
|
|
74
|
+
|
|
75
|
+
- Do not add a hard dependency on a specific observability vendor.
|
|
76
|
+
- Do not require OTel configuration for logs or timings to function.
|
|
77
|
+
- Do not duplicate framework behavior in the core runtime.
|
|
78
|
+
- Do not swallow application exceptions from observed code.
|
|
79
|
+
- Do not use `[codex]`, `[claude]`, `[copilot]`, or other agent labels in PR
|
|
80
|
+
titles.
|
|
@@ -306,6 +306,11 @@ class ObservabilityRuntime:
|
|
|
306
306
|
context: RequestObservabilityContext,
|
|
307
307
|
) -> None:
|
|
308
308
|
try:
|
|
309
|
+
parent_operation = _OPERATION_CONTEXT.get()
|
|
310
|
+
timings = context.timings_ms
|
|
311
|
+
if context.internal_dispatch and parent_operation is not None:
|
|
312
|
+
timings = parent_operation.timings_ms
|
|
313
|
+
context.timings_ms = timings
|
|
309
314
|
operation = OperationObservabilityContext(
|
|
310
315
|
config=context.config,
|
|
311
316
|
name=context.route,
|
|
@@ -316,7 +321,7 @@ class ObservabilityRuntime:
|
|
|
316
321
|
"endpoint": context.endpoint,
|
|
317
322
|
"path": context.path,
|
|
318
323
|
},
|
|
319
|
-
timings_ms=
|
|
324
|
+
timings_ms=timings,
|
|
320
325
|
emit_log=False,
|
|
321
326
|
record_metric=False,
|
|
322
327
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "policyengine-observability"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "Shared PolicyEngine observability runtime for logs, timings, metrics, and OpenTelemetry."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "PolicyEngine", email = "hello@policyengine.org" }]
|
|
@@ -628,6 +628,78 @@ def test_request_lifecycle_records_headers_and_context_metrics() -> None:
|
|
|
628
628
|
assert observed.requests.calls[0][1] == 1
|
|
629
629
|
|
|
630
630
|
|
|
631
|
+
def test_internal_dispatch_segments_merge_into_parent_operation() -> None:
|
|
632
|
+
observed = runtime()
|
|
633
|
+
handle = observed.start_operation(
|
|
634
|
+
"modal_worker_dispatch",
|
|
635
|
+
flavor="modal_worker",
|
|
636
|
+
)
|
|
637
|
+
parent_operation = handle["operation"]
|
|
638
|
+
context = RequestObservabilityContext(
|
|
639
|
+
config=observed.config,
|
|
640
|
+
request_id="request-1",
|
|
641
|
+
method="POST",
|
|
642
|
+
route="/calculate",
|
|
643
|
+
path="/calculate",
|
|
644
|
+
endpoint="calculate",
|
|
645
|
+
query_keys=[],
|
|
646
|
+
content_length_bytes=None,
|
|
647
|
+
inbound={},
|
|
648
|
+
internal_dispatch=True,
|
|
649
|
+
)
|
|
650
|
+
|
|
651
|
+
try:
|
|
652
|
+
observed.begin_request(context)
|
|
653
|
+
with observed.segment(SegmentName.LOAD):
|
|
654
|
+
pass
|
|
655
|
+
observed.finish_request(200)
|
|
656
|
+
observed.teardown_request(None)
|
|
657
|
+
|
|
658
|
+
assert context.timings_ms is parent_operation.timings_ms
|
|
659
|
+
assert "load" in parent_operation.timings_ms
|
|
660
|
+
assert observed.current_operation() is parent_operation
|
|
661
|
+
finally:
|
|
662
|
+
observed.end_operation(handle)
|
|
663
|
+
|
|
664
|
+
assert observed.current_context() is None
|
|
665
|
+
assert observed.current_operation() is None
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
def test_non_internal_request_timings_do_not_leak_to_parent_operation() -> (
|
|
669
|
+
None
|
|
670
|
+
):
|
|
671
|
+
observed = runtime()
|
|
672
|
+
handle = observed.start_operation("job", flavor="worker")
|
|
673
|
+
parent_operation = handle["operation"]
|
|
674
|
+
context = RequestObservabilityContext(
|
|
675
|
+
config=observed.config,
|
|
676
|
+
request_id="request-1",
|
|
677
|
+
method="POST",
|
|
678
|
+
route="/calculate",
|
|
679
|
+
path="/calculate",
|
|
680
|
+
endpoint="calculate",
|
|
681
|
+
query_keys=[],
|
|
682
|
+
content_length_bytes=None,
|
|
683
|
+
inbound={},
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
try:
|
|
687
|
+
observed.begin_request(context)
|
|
688
|
+
with observed.segment(SegmentName.LOAD):
|
|
689
|
+
pass
|
|
690
|
+
observed.finish_request(200)
|
|
691
|
+
observed.teardown_request(None)
|
|
692
|
+
|
|
693
|
+
assert context.timings_ms is not parent_operation.timings_ms
|
|
694
|
+
assert "load" not in parent_operation.timings_ms
|
|
695
|
+
assert observed.current_operation() is parent_operation
|
|
696
|
+
finally:
|
|
697
|
+
observed.end_operation(handle)
|
|
698
|
+
|
|
699
|
+
assert observed.current_context() is None
|
|
700
|
+
assert observed.current_operation() is None
|
|
701
|
+
|
|
702
|
+
|
|
631
703
|
def test_request_methods_noop_without_current_context() -> None:
|
|
632
704
|
observed = runtime()
|
|
633
705
|
|
{policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.github/bump_version.py
RENAMED
|
File without changes
|
{policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.github/check-changelog.sh
RENAMED
|
File without changes
|
{policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.github/fetch_version.py
RENAMED
|
File without changes
|
{policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.github/get-changelog-diff.sh
RENAMED
|
File without changes
|
{policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.github/publish-git-tag.sh
RENAMED
|
File without changes
|
{policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.github/workflows/pr.yml
RENAMED
|
File without changes
|
{policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/.github/workflows/push.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/tests/test_fastapi_adapter.py
RENAMED
|
File without changes
|
{policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/tests/test_flask_adapter.py
RENAMED
|
File without changes
|
{policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/tests/test_public_api.py
RENAMED
|
File without changes
|
{policyengine_observability-0.2.1 → policyengine_observability-0.3.0}/tests/test_release_scripts.py
RENAMED
|
File without changes
|