icv-trace 0.1.0rc4__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.
- icv_trace-0.1.0rc4/CHANGELOG.md +80 -0
- icv_trace-0.1.0rc4/CONTRIBUTING.md +40 -0
- icv_trace-0.1.0rc4/LICENSE +21 -0
- icv_trace-0.1.0rc4/MANIFEST.in +9 -0
- icv_trace-0.1.0rc4/PKG-INFO +220 -0
- icv_trace-0.1.0rc4/PROVENANCE.md +40 -0
- icv_trace-0.1.0rc4/README.md +191 -0
- icv_trace-0.1.0rc4/RELEASING.md +58 -0
- icv_trace-0.1.0rc4/VERIFICATION.md +29 -0
- icv_trace-0.1.0rc4/docs/README.md +16 -0
- icv_trace-0.1.0rc4/docs/contracts.md +122 -0
- icv_trace-0.1.0rc4/docs/human-renderer.md +99 -0
- icv_trace-0.1.0rc4/examples/human_trace.py +156 -0
- icv_trace-0.1.0rc4/pyproject.toml +64 -0
- icv_trace-0.1.0rc4/setup.cfg +4 -0
- icv_trace-0.1.0rc4/src/icv_trace/__init__.py +45 -0
- icv_trace-0.1.0rc4/src/icv_trace/adapters.py +110 -0
- icv_trace-0.1.0rc4/src/icv_trace/core.py +919 -0
- icv_trace-0.1.0rc4/src/icv_trace/human.py +305 -0
- icv_trace-0.1.0rc4/src/icv_trace/py.typed +1 -0
- icv_trace-0.1.0rc4/src/icv_trace.egg-info/PKG-INFO +220 -0
- icv_trace-0.1.0rc4/src/icv_trace.egg-info/SOURCES.txt +23 -0
- icv_trace-0.1.0rc4/src/icv_trace.egg-info/dependency_links.txt +1 -0
- icv_trace-0.1.0rc4/src/icv_trace.egg-info/requires.txt +5 -0
- icv_trace-0.1.0rc4/src/icv_trace.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.1.0rc4] - 2026-09-09
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Prepared the CI and tag workflow for public PyPI publishing through a PyPI
|
|
12
|
+
trusted publisher on GitHub-hosted runners. The release gate now also checks
|
|
13
|
+
that `human.py` is included in the wheel.
|
|
14
|
+
- Added self-contained public contracts and renderer guides, contributor and
|
|
15
|
+
release instructions, and source-distribution copies of docs and examples.
|
|
16
|
+
|
|
17
|
+
### Consumer behaviour
|
|
18
|
+
|
|
19
|
+
- Runtime behaviour and the supported package-root API are unchanged from
|
|
20
|
+
`0.1.0rc3`. This release moves the distribution target to PyPI. Private-index
|
|
21
|
+
consumers should validate an explicit `0.1.0rc4` PyPI install before removing
|
|
22
|
+
their private extra index, and retain that index while other private packages
|
|
23
|
+
still need it.
|
|
24
|
+
|
|
25
|
+
## [0.1.0rc3] - 2026-09-09
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- Added `HumanRenderer` and `HumanTraceSink` for bounded, readable projection
|
|
30
|
+
and optional exact JSONL fanout of trace records.
|
|
31
|
+
|
|
32
|
+
### Consumer behaviour
|
|
33
|
+
|
|
34
|
+
- Existing trace bindings, canonical human output, structured records and
|
|
35
|
+
admission budgets retain their behaviour. Readable rendering is opt-in:
|
|
36
|
+
hosts bind `HumanTraceSink` as the structured destination and inspect its
|
|
37
|
+
output health separately from core health. Its human view omits correlation
|
|
38
|
+
IDs; optional JSONL fanout preserves the original records. The core charges
|
|
39
|
+
structured bytes, not downstream presentation/fanout bytes.
|
|
40
|
+
|
|
41
|
+
## [0.1.0rc2] - 2026-09-09
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- Added explicit Python setup before the private publish workflow validates
|
|
46
|
+
release metadata. `v0.1.0rc1` stopped in that pre-upload job because the
|
|
47
|
+
bare self-hosted runner had no `python` command; no distribution was
|
|
48
|
+
uploaded and the trace API is unchanged.
|
|
49
|
+
|
|
50
|
+
## [0.1.0rc1] - 2026-09-09
|
|
51
|
+
|
|
52
|
+
### Unpublished
|
|
53
|
+
|
|
54
|
+
- `v0.1.0rc1` did not upload a distribution. It remains historical evidence
|
|
55
|
+
of the failed pre-upload workflow run and must not be retagged.
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
|
|
59
|
+
- Extracted the pure-Python trace core from icvlocal at source commit
|
|
60
|
+
`087479b96929a56447c2ac8011011023b4df2dcd`.
|
|
61
|
+
- Added bounded lifecycle diagnostics, correlation context, sink health and
|
|
62
|
+
same-thread callback capture as the initial private release candidate.
|
|
63
|
+
|
|
64
|
+
### Changed
|
|
65
|
+
|
|
66
|
+
- Existing incubated consumers keep the same public API and runtime behaviour.
|
|
67
|
+
The implementation is now distributed as `icv-trace` for index installation.
|
|
68
|
+
|
|
69
|
+
### Release gate record
|
|
70
|
+
|
|
71
|
+
- The release owner completes and records all six private release gates on the
|
|
72
|
+
exact tagged commit before pushing `v0.1.0rc2`: version and changelog,
|
|
73
|
+
green CI, clean publish-workflow simulation, artefact inspection and clean
|
|
74
|
+
installation, tagged-workflow review, and consumer-facing behaviour notes.
|
|
75
|
+
|
|
76
|
+
[Unreleased]: https://github.com/icvoss/icv-trace/compare/v0.1.0rc4...HEAD
|
|
77
|
+
[0.1.0rc4]: https://github.com/icvoss/icv-trace/releases/tag/v0.1.0rc4
|
|
78
|
+
[0.1.0rc3]: https://github.com/icvoss/icv-trace/releases/tag/v0.1.0rc3
|
|
79
|
+
[0.1.0rc2]: https://github.com/icvoss/icv-trace/releases/tag/v0.1.0rc2
|
|
80
|
+
[0.1.0rc1]: https://github.com/icvoss/icv-trace/releases/tag/v0.1.0rc1
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Contributing to icv-trace
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
- Python 3.11 or later
|
|
6
|
+
- `uv` or pip
|
|
7
|
+
|
|
8
|
+
The package is pure Python. Django, a database and ICV sibling packages are
|
|
9
|
+
not required for development or tests.
|
|
10
|
+
|
|
11
|
+
## Local setup and checks
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
python -m venv .venv
|
|
15
|
+
source .venv/bin/activate
|
|
16
|
+
pip install -e ".[dev]"
|
|
17
|
+
ruff check .
|
|
18
|
+
ruff format --check .
|
|
19
|
+
mypy src/icv_trace
|
|
20
|
+
pytest tests -v --tb=short
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Use conventional commits and work on a branch. Public behaviour is the
|
|
24
|
+
package-root API and the `icv-trace.*.v1` contracts in
|
|
25
|
+
[docs/contracts.md](docs/contracts.md). Preserve caller ownership of domain
|
|
26
|
+
truth and safe facts, and host ownership of settings, transport, audit and
|
|
27
|
+
error reporting. Update the normative package contracts in `docs/` alongside
|
|
28
|
+
any change to supported behaviour.
|
|
29
|
+
|
|
30
|
+
## Test modes
|
|
31
|
+
|
|
32
|
+
The normal suite supports an editable installation. CI additionally runs a
|
|
33
|
+
built-wheel job with `ICV_TRACE_REQUIRE_WHEEL=1`; it removes `src` from the
|
|
34
|
+
import path and fails unless `icv_trace` resolves from `site-packages`.
|
|
35
|
+
|
|
36
|
+
## Releasing
|
|
37
|
+
|
|
38
|
+
See [RELEASING.md](RELEASING.md). A `v<version>` tag publishes to PyPI, so it
|
|
39
|
+
is pushed only after all release gates pass on that exact commit. The release
|
|
40
|
+
guide also records the transition for existing private-index consumers.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nigel Copley
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: icv-trace
|
|
3
|
+
Version: 0.1.0rc4
|
|
4
|
+
Summary: Bounded operation diagnostics, correlation and output health for Python hosts
|
|
5
|
+
Author: Nigel Copley
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/icvoss/icv-trace
|
|
8
|
+
Project-URL: Documentation, https://github.com/icvoss/icv-trace/tree/main/docs
|
|
9
|
+
Project-URL: Changelog, https://github.com/icvoss/icv-trace/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Issue Tracker, https://github.com/icvoss/icv-trace/issues
|
|
11
|
+
Project-URL: Source, https://github.com/icvoss/icv-trace
|
|
12
|
+
Keywords: diagnostics,observability,trace,tracing
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: mypy>=1.11; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
27
|
+
Requires-Dist: ruff>=0.15; extra == "dev"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# icv-trace
|
|
31
|
+
|
|
32
|
+
`icv-trace` is a pure-Python diagnostic boundary for meaningful operations.
|
|
33
|
+
It creates correlation identity, attempts immediate baseline lifecycle records,
|
|
34
|
+
admits bounded detail when enabled, and exposes local output health. It has no
|
|
35
|
+
Django, database, ICV-package, logger or exporter dependency.
|
|
36
|
+
|
|
37
|
+
The package consumes caller-selected safe scalar facts, a `TracePolicy`,
|
|
38
|
+
`TraceSinks`, and optional parent or retry context. It produces human and
|
|
39
|
+
JSONL records plus local sink health. The caller and host retain ownership of
|
|
40
|
+
domain truth, persistence, policy resolution, destinations, propagation,
|
|
41
|
+
error reporting, metrics and audit.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
python -m pip install "icv-trace==0.1.0rc4"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Public release candidates use PyPI and an explicit version as shown above.
|
|
50
|
+
`0.1.0rc3` remains available from the private index at `pypi.icvoss.com` for
|
|
51
|
+
existing consumers. Validate the explicit PyPI version before removing a
|
|
52
|
+
private extra index, and retain that index while any other private dependency
|
|
53
|
+
requires it. Install by distribution name, never by a sibling path.
|
|
54
|
+
|
|
55
|
+
## Use
|
|
56
|
+
|
|
57
|
+
The default `trace()` binding writes baseline records to stderr. It reads the
|
|
58
|
+
documented `TRACE_*` environment values and has detail disabled by default.
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from icv_trace import trace
|
|
62
|
+
|
|
63
|
+
with trace("catalogue.import", source="supplier") as operation:
|
|
64
|
+
operation.note("rows_loaded", count=12)
|
|
65
|
+
operation.result(imported=12, rejected=0)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
For explicit policy and separate human and JSONL destinations, a host binds a
|
|
69
|
+
callable. Sinks receive one complete rendered line and own writing, buffering
|
|
70
|
+
and flushing it.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import sys
|
|
74
|
+
|
|
75
|
+
from icv_trace import TracePolicy, TraceSinks, bind_trace, textio_sink
|
|
76
|
+
|
|
77
|
+
policy = TracePolicy(
|
|
78
|
+
TRACE_ENABLED=True,
|
|
79
|
+
TRACE_DESTINATIONS=("human", "structured"),
|
|
80
|
+
TRACE_MAX_DETAIL_EVENTS=100,
|
|
81
|
+
TRACE_MAX_BYTES=65536,
|
|
82
|
+
)
|
|
83
|
+
with open("trace.jsonl", "x", encoding="utf-8") as jsonl_file:
|
|
84
|
+
trace_scan = bind_trace(
|
|
85
|
+
lambda: policy,
|
|
86
|
+
TraceSinks(human=textio_sink(sys.stderr), structured=textio_sink(jsonl_file)),
|
|
87
|
+
)
|
|
88
|
+
with trace_scan("scan") as operation:
|
|
89
|
+
operation.note("classification", matched=4)
|
|
90
|
+
operation.result(completed=True)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`TRACE_ENABLED` controls additional detail only. Baseline START and END are
|
|
94
|
+
attempted independently of `TRACE_ENABLED`, `DEBUG` and logging levels.
|
|
95
|
+
`TextIOSink` rejects short writes and never flushes or closes the caller's
|
|
96
|
+
stream. Logging, Eliot and OpenTelemetry trial adapters are retained as
|
|
97
|
+
internal comparison code in `icv_trace.adapters`; they are not supported
|
|
98
|
+
public delivery APIs.
|
|
99
|
+
|
|
100
|
+
## Public API and contract
|
|
101
|
+
|
|
102
|
+
The supported package-root exports are `trace`, `bind_trace`,
|
|
103
|
+
`current_trace_context`, `emit_incomplete`, `capture_callback`, `TracePolicy`,
|
|
104
|
+
`TraceSinks`, `TraceContext`, `TraceAttempt`, `TraceIncomplete`,
|
|
105
|
+
`TraceOutputHealth`, `TraceHandle`, `TextIOSink`, `textio_sink`,
|
|
106
|
+
`HumanRenderer`, `HumanTraceSink`, `TraceInputError`,
|
|
107
|
+
`TraceConfigurationError` and `TraceSinkError`. The complete v1 input, output,
|
|
108
|
+
failure and compatibility contracts are in [docs/contracts.md](docs/contracts.md).
|
|
109
|
+
|
|
110
|
+
## Readable projection of structured traces
|
|
111
|
+
|
|
112
|
+
`HumanRenderer` turns admitted JSONL records into compact, indented terminal
|
|
113
|
+
lines. It has no Django or worker dependency and only retains a bounded set of
|
|
114
|
+
open span identities for indentation. `HumanTraceSink` makes this useful with
|
|
115
|
+
the existing core: bind the core to its `structured` input, then it writes the
|
|
116
|
+
readable line and optionally forwards the original JSONL line unchanged.
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
import sys
|
|
120
|
+
from secrets import token_hex
|
|
121
|
+
|
|
122
|
+
from icv_trace import (
|
|
123
|
+
HumanTraceSink, TraceContext, TraceIncomplete, TracePolicy, TraceSinks,
|
|
124
|
+
bind_trace, emit_incomplete, textio_sink, trace,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
human_sink = HumanTraceSink(human=textio_sink(sys.stderr))
|
|
128
|
+
policy = TracePolicy(TRACE_ENABLED=True, TRACE_DESTINATIONS=("structured",))
|
|
129
|
+
run = bind_trace(lambda: policy, TraceSinks(structured=human_sink))
|
|
130
|
+
|
|
131
|
+
with run("example.command", entrypoint="management_command") as command:
|
|
132
|
+
with trace("example.pipeline"):
|
|
133
|
+
with trace("example.service") as service:
|
|
134
|
+
service.note("loaded", count=2)
|
|
135
|
+
service.result(count=2)
|
|
136
|
+
command.result(completed=True)
|
|
137
|
+
command_context = command.context
|
|
138
|
+
|
|
139
|
+
# This simulates a supervisor observation of a context it saved at dispatch.
|
|
140
|
+
# A missing END alone never authorises an incomplete record.
|
|
141
|
+
assert command_context is not None
|
|
142
|
+
remote_context = TraceContext(command_context.trace_id, token_hex(8), token_hex(16), command_context.span_id)
|
|
143
|
+
emit_incomplete(
|
|
144
|
+
TraceIncomplete(remote_context, "example.worker.task", "worker_lost"),
|
|
145
|
+
policy=policy,
|
|
146
|
+
sinks=TraceSinks(structured=human_sink),
|
|
147
|
+
)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Labels can replace operation names for a particular host:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from icv_trace import HumanRenderer
|
|
154
|
+
|
|
155
|
+
renderer = HumanRenderer(labels={"example.command": "import command"})
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Retry and continuation records include their attempt kind and number in this
|
|
159
|
+
view. Correlation IDs remain in the original JSONL rather than the compact
|
|
160
|
+
line. `HumanTraceSink.output_health` is sticky and must be checked separately
|
|
161
|
+
from core trace health because the core sees the composite as one destination.
|
|
162
|
+
The host owns prompt flushing: `textio_sink()` does not flush or close the
|
|
163
|
+
stream. The human and optional JSONL callables must be different underlying
|
|
164
|
+
channels. The adapter rejects the same callable object, but cannot detect two
|
|
165
|
+
wrappers around one stream. Do not make either destination call the same
|
|
166
|
+
`HumanTraceSink` recursively. If the host's warning channel is also broken,
|
|
167
|
+
it cannot report that diagnostic failure elsewhere.
|
|
168
|
+
|
|
169
|
+
The runnable dependency-free example mirrors a command invoking a pipeline of
|
|
170
|
+
services. It writes compact output to stderr, can fan the original JSONL to a
|
|
171
|
+
new file, and labels the remote incomplete record as an explicit simulated
|
|
172
|
+
supervisor observation rather than an inference from a missing END. The
|
|
173
|
+
[human-renderer guide](docs/human-renderer.md) covers labels, bounded nesting,
|
|
174
|
+
fanout, health and host-owned task supervision.
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
python examples/human_trace.py
|
|
178
|
+
python examples/human_trace.py --detail --jsonl-file trace.jsonl
|
|
179
|
+
python examples/human_trace.py --fail
|
|
180
|
+
python examples/human_trace.py --simulate-worker-lost
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The same-thread `capture_callback()` bridge is bounded to its live owner and
|
|
184
|
+
creator thread. It does not register callbacks or transfer work across
|
|
185
|
+
threads or processes.
|
|
186
|
+
|
|
187
|
+
## Development
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
python -m venv .venv
|
|
191
|
+
source .venv/bin/activate
|
|
192
|
+
pip install -e ".[dev]"
|
|
193
|
+
ruff check .
|
|
194
|
+
ruff format --check .
|
|
195
|
+
mypy src/icv_trace
|
|
196
|
+
pytest tests -v --tb=short
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The normal development suite permits the editable install. CI and release
|
|
200
|
+
verification also build a wheel, install it without the source tree on the
|
|
201
|
+
import path, and assert that `icv_trace` resolves from `site-packages`.
|
|
202
|
+
|
|
203
|
+
## Documentation
|
|
204
|
+
|
|
205
|
+
- [Contracts](docs/contracts.md): v1 inputs, policy and limits, records,
|
|
206
|
+
output health, task context and compatibility.
|
|
207
|
+
- [Human renderer](docs/human-renderer.md): readable services, commands,
|
|
208
|
+
pipelines and task observations.
|
|
209
|
+
- [Example](examples/human_trace.py): runnable dependency-free program.
|
|
210
|
+
- [Contributing](CONTRIBUTING.md), [verification](VERIFICATION.md), and
|
|
211
|
+
[releasing](RELEASING.md): development and publication practice.
|
|
212
|
+
|
|
213
|
+
## Provenance
|
|
214
|
+
|
|
215
|
+
This package extracts the site-owned incubation core from icvlocal commit
|
|
216
|
+
`087479b96929a56447c2ac8011011023b4df2dcd`. See
|
|
217
|
+
[PROVENANCE.md](PROVENANCE.md) for historical source, retained hashes and
|
|
218
|
+
extraction adaptations. The public package documentation above is sufficient
|
|
219
|
+
to install and use the distribution; the historical material is not a runtime
|
|
220
|
+
dependency.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Historical extraction provenance
|
|
2
|
+
|
|
3
|
+
This record explains the private incubation history. It is not required to
|
|
4
|
+
install or use the package. Current public contracts are in
|
|
5
|
+
[`docs/contracts.md`](docs/contracts.md) and
|
|
6
|
+
[`docs/human-renderer.md`](docs/human-renderer.md).
|
|
7
|
+
|
|
8
|
+
The package was extracted from site commit
|
|
9
|
+
`087479b96929a56447c2ac8011011023b4df2dcd`, which includes the initial trial
|
|
10
|
+
source, the local module-name substitution and the bounded callback-capture
|
|
11
|
+
extension. The archived source remains under
|
|
12
|
+
`docs/reviews/trace-adapter-trial-2026-09-08/` in the ICV OSS umbrella.
|
|
13
|
+
|
|
14
|
+
| Archived file | SHA-256 |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `runtime/src/trace_trial/core.py` | `c28fbce7140fb5a1e1b35b82d0bb180ca2c0407969ca781f8a023d8f494638b5` |
|
|
17
|
+
| `runtime/src/trace_trial/adapters.py` | `8734ed435e3c2de4d5053f4ffe6aacc41bbbd72f5cb2fe52ccfaffbb9fd8a489` |
|
|
18
|
+
| `checks/conftest.py` | `34989ba7b5e7d738d79746a12e448d004305c13c8f24e299bc238eef38107c1b` |
|
|
19
|
+
| `checks/test_admission_precision.py` | `2c5d785fe76ad8d53a871c8db1c01cda2e9006d0c57c967aa62004b2ce10bba3` |
|
|
20
|
+
| `checks/test_core_hardening.py` | `6c6c6f1662d69184613733590a23ca1ac9896d2d6180d2c776fdd7fb450f395d` |
|
|
21
|
+
| `checks/test_invalid_child_admission.py` | `7d156e1dd344b1e1ca62916329b049df02ed49a0572294b00e1247629b29623b` |
|
|
22
|
+
| `checks/test_trace_contract.py` | `f929a0d2b31e8a3ab7e451c66cba40a535fa1d7cc0ac4fe18aba3abcecf0bd34` |
|
|
23
|
+
| `checks/test_trace_edges.py` | `7e359d32d57afc4d7f0cd3a258f5944f830a33c8b36fc13c55283a4b464547f2` |
|
|
24
|
+
|
|
25
|
+
The extracted files are not byte-identical to the archive. The module import
|
|
26
|
+
was substituted from `trace_trial` to `icv_trace`, and the callback bridge was
|
|
27
|
+
added during incubation. Extraction adds packaging metadata, public module
|
|
28
|
+
docstrings and `__version__`, and replaces the former `ICV_TRACE_SRC`
|
|
29
|
+
source-mode fixture with a built-wheel origin guard. It also adds
|
|
30
|
+
non-behavioural annotations and casts required by the blocking mypy gate, and
|
|
31
|
+
removes one unused test import. The archived hashes above remain provenance for
|
|
32
|
+
the source history; they are not hashes of the extracted files.
|
|
33
|
+
|
|
34
|
+
At extraction time, the rejected comparison-adapter delivery selector was not
|
|
35
|
+
a supported API. The historical statement that `TextIOSink` and `textio_sink`
|
|
36
|
+
were the only supported delivery helpers predates the released human renderer.
|
|
37
|
+
Today, `HumanRenderer` and `HumanTraceSink` are supported package-root APIs;
|
|
38
|
+
the complete current surface is documented in the README. Logging, Eliot and
|
|
39
|
+
OpenTelemetry helpers remain experimental comparison code, with no delivery or
|
|
40
|
+
conformance promise.
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# icv-trace
|
|
2
|
+
|
|
3
|
+
`icv-trace` is a pure-Python diagnostic boundary for meaningful operations.
|
|
4
|
+
It creates correlation identity, attempts immediate baseline lifecycle records,
|
|
5
|
+
admits bounded detail when enabled, and exposes local output health. It has no
|
|
6
|
+
Django, database, ICV-package, logger or exporter dependency.
|
|
7
|
+
|
|
8
|
+
The package consumes caller-selected safe scalar facts, a `TracePolicy`,
|
|
9
|
+
`TraceSinks`, and optional parent or retry context. It produces human and
|
|
10
|
+
JSONL records plus local sink health. The caller and host retain ownership of
|
|
11
|
+
domain truth, persistence, policy resolution, destinations, propagation,
|
|
12
|
+
error reporting, metrics and audit.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
python -m pip install "icv-trace==0.1.0rc4"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Public release candidates use PyPI and an explicit version as shown above.
|
|
21
|
+
`0.1.0rc3` remains available from the private index at `pypi.icvoss.com` for
|
|
22
|
+
existing consumers. Validate the explicit PyPI version before removing a
|
|
23
|
+
private extra index, and retain that index while any other private dependency
|
|
24
|
+
requires it. Install by distribution name, never by a sibling path.
|
|
25
|
+
|
|
26
|
+
## Use
|
|
27
|
+
|
|
28
|
+
The default `trace()` binding writes baseline records to stderr. It reads the
|
|
29
|
+
documented `TRACE_*` environment values and has detail disabled by default.
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from icv_trace import trace
|
|
33
|
+
|
|
34
|
+
with trace("catalogue.import", source="supplier") as operation:
|
|
35
|
+
operation.note("rows_loaded", count=12)
|
|
36
|
+
operation.result(imported=12, rejected=0)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
For explicit policy and separate human and JSONL destinations, a host binds a
|
|
40
|
+
callable. Sinks receive one complete rendered line and own writing, buffering
|
|
41
|
+
and flushing it.
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import sys
|
|
45
|
+
|
|
46
|
+
from icv_trace import TracePolicy, TraceSinks, bind_trace, textio_sink
|
|
47
|
+
|
|
48
|
+
policy = TracePolicy(
|
|
49
|
+
TRACE_ENABLED=True,
|
|
50
|
+
TRACE_DESTINATIONS=("human", "structured"),
|
|
51
|
+
TRACE_MAX_DETAIL_EVENTS=100,
|
|
52
|
+
TRACE_MAX_BYTES=65536,
|
|
53
|
+
)
|
|
54
|
+
with open("trace.jsonl", "x", encoding="utf-8") as jsonl_file:
|
|
55
|
+
trace_scan = bind_trace(
|
|
56
|
+
lambda: policy,
|
|
57
|
+
TraceSinks(human=textio_sink(sys.stderr), structured=textio_sink(jsonl_file)),
|
|
58
|
+
)
|
|
59
|
+
with trace_scan("scan") as operation:
|
|
60
|
+
operation.note("classification", matched=4)
|
|
61
|
+
operation.result(completed=True)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`TRACE_ENABLED` controls additional detail only. Baseline START and END are
|
|
65
|
+
attempted independently of `TRACE_ENABLED`, `DEBUG` and logging levels.
|
|
66
|
+
`TextIOSink` rejects short writes and never flushes or closes the caller's
|
|
67
|
+
stream. Logging, Eliot and OpenTelemetry trial adapters are retained as
|
|
68
|
+
internal comparison code in `icv_trace.adapters`; they are not supported
|
|
69
|
+
public delivery APIs.
|
|
70
|
+
|
|
71
|
+
## Public API and contract
|
|
72
|
+
|
|
73
|
+
The supported package-root exports are `trace`, `bind_trace`,
|
|
74
|
+
`current_trace_context`, `emit_incomplete`, `capture_callback`, `TracePolicy`,
|
|
75
|
+
`TraceSinks`, `TraceContext`, `TraceAttempt`, `TraceIncomplete`,
|
|
76
|
+
`TraceOutputHealth`, `TraceHandle`, `TextIOSink`, `textio_sink`,
|
|
77
|
+
`HumanRenderer`, `HumanTraceSink`, `TraceInputError`,
|
|
78
|
+
`TraceConfigurationError` and `TraceSinkError`. The complete v1 input, output,
|
|
79
|
+
failure and compatibility contracts are in [docs/contracts.md](docs/contracts.md).
|
|
80
|
+
|
|
81
|
+
## Readable projection of structured traces
|
|
82
|
+
|
|
83
|
+
`HumanRenderer` turns admitted JSONL records into compact, indented terminal
|
|
84
|
+
lines. It has no Django or worker dependency and only retains a bounded set of
|
|
85
|
+
open span identities for indentation. `HumanTraceSink` makes this useful with
|
|
86
|
+
the existing core: bind the core to its `structured` input, then it writes the
|
|
87
|
+
readable line and optionally forwards the original JSONL line unchanged.
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
import sys
|
|
91
|
+
from secrets import token_hex
|
|
92
|
+
|
|
93
|
+
from icv_trace import (
|
|
94
|
+
HumanTraceSink, TraceContext, TraceIncomplete, TracePolicy, TraceSinks,
|
|
95
|
+
bind_trace, emit_incomplete, textio_sink, trace,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
human_sink = HumanTraceSink(human=textio_sink(sys.stderr))
|
|
99
|
+
policy = TracePolicy(TRACE_ENABLED=True, TRACE_DESTINATIONS=("structured",))
|
|
100
|
+
run = bind_trace(lambda: policy, TraceSinks(structured=human_sink))
|
|
101
|
+
|
|
102
|
+
with run("example.command", entrypoint="management_command") as command:
|
|
103
|
+
with trace("example.pipeline"):
|
|
104
|
+
with trace("example.service") as service:
|
|
105
|
+
service.note("loaded", count=2)
|
|
106
|
+
service.result(count=2)
|
|
107
|
+
command.result(completed=True)
|
|
108
|
+
command_context = command.context
|
|
109
|
+
|
|
110
|
+
# This simulates a supervisor observation of a context it saved at dispatch.
|
|
111
|
+
# A missing END alone never authorises an incomplete record.
|
|
112
|
+
assert command_context is not None
|
|
113
|
+
remote_context = TraceContext(command_context.trace_id, token_hex(8), token_hex(16), command_context.span_id)
|
|
114
|
+
emit_incomplete(
|
|
115
|
+
TraceIncomplete(remote_context, "example.worker.task", "worker_lost"),
|
|
116
|
+
policy=policy,
|
|
117
|
+
sinks=TraceSinks(structured=human_sink),
|
|
118
|
+
)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Labels can replace operation names for a particular host:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from icv_trace import HumanRenderer
|
|
125
|
+
|
|
126
|
+
renderer = HumanRenderer(labels={"example.command": "import command"})
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Retry and continuation records include their attempt kind and number in this
|
|
130
|
+
view. Correlation IDs remain in the original JSONL rather than the compact
|
|
131
|
+
line. `HumanTraceSink.output_health` is sticky and must be checked separately
|
|
132
|
+
from core trace health because the core sees the composite as one destination.
|
|
133
|
+
The host owns prompt flushing: `textio_sink()` does not flush or close the
|
|
134
|
+
stream. The human and optional JSONL callables must be different underlying
|
|
135
|
+
channels. The adapter rejects the same callable object, but cannot detect two
|
|
136
|
+
wrappers around one stream. Do not make either destination call the same
|
|
137
|
+
`HumanTraceSink` recursively. If the host's warning channel is also broken,
|
|
138
|
+
it cannot report that diagnostic failure elsewhere.
|
|
139
|
+
|
|
140
|
+
The runnable dependency-free example mirrors a command invoking a pipeline of
|
|
141
|
+
services. It writes compact output to stderr, can fan the original JSONL to a
|
|
142
|
+
new file, and labels the remote incomplete record as an explicit simulated
|
|
143
|
+
supervisor observation rather than an inference from a missing END. The
|
|
144
|
+
[human-renderer guide](docs/human-renderer.md) covers labels, bounded nesting,
|
|
145
|
+
fanout, health and host-owned task supervision.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
python examples/human_trace.py
|
|
149
|
+
python examples/human_trace.py --detail --jsonl-file trace.jsonl
|
|
150
|
+
python examples/human_trace.py --fail
|
|
151
|
+
python examples/human_trace.py --simulate-worker-lost
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The same-thread `capture_callback()` bridge is bounded to its live owner and
|
|
155
|
+
creator thread. It does not register callbacks or transfer work across
|
|
156
|
+
threads or processes.
|
|
157
|
+
|
|
158
|
+
## Development
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
python -m venv .venv
|
|
162
|
+
source .venv/bin/activate
|
|
163
|
+
pip install -e ".[dev]"
|
|
164
|
+
ruff check .
|
|
165
|
+
ruff format --check .
|
|
166
|
+
mypy src/icv_trace
|
|
167
|
+
pytest tests -v --tb=short
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The normal development suite permits the editable install. CI and release
|
|
171
|
+
verification also build a wheel, install it without the source tree on the
|
|
172
|
+
import path, and assert that `icv_trace` resolves from `site-packages`.
|
|
173
|
+
|
|
174
|
+
## Documentation
|
|
175
|
+
|
|
176
|
+
- [Contracts](docs/contracts.md): v1 inputs, policy and limits, records,
|
|
177
|
+
output health, task context and compatibility.
|
|
178
|
+
- [Human renderer](docs/human-renderer.md): readable services, commands,
|
|
179
|
+
pipelines and task observations.
|
|
180
|
+
- [Example](examples/human_trace.py): runnable dependency-free program.
|
|
181
|
+
- [Contributing](CONTRIBUTING.md), [verification](VERIFICATION.md), and
|
|
182
|
+
[releasing](RELEASING.md): development and publication practice.
|
|
183
|
+
|
|
184
|
+
## Provenance
|
|
185
|
+
|
|
186
|
+
This package extracts the site-owned incubation core from icvlocal commit
|
|
187
|
+
`087479b96929a56447c2ac8011011023b4df2dcd`. See
|
|
188
|
+
[PROVENANCE.md](PROVENANCE.md) for historical source, retained hashes and
|
|
189
|
+
extraction adaptations. The public package documentation above is sufficient
|
|
190
|
+
to install and use the distribution; the historical material is not a runtime
|
|
191
|
+
dependency.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Releasing icv-trace
|
|
2
|
+
|
|
3
|
+
`0.1.0rc3` remains available from the private index at `pypi.icvoss.com`.
|
|
4
|
+
The public repository publishes new releases to PyPI through the GitHub Actions
|
|
5
|
+
trusted publisher.
|
|
6
|
+
Pushing a `v<semver>` tag is irreversible and must point to the merged `main`
|
|
7
|
+
commit.
|
|
8
|
+
|
|
9
|
+
This is a pure-Python package. There are no Django, database or migration
|
|
10
|
+
legs, but static, built-wheel and clean-environment checks are required.
|
|
11
|
+
|
|
12
|
+
## Before tagging
|
|
13
|
+
|
|
14
|
+
Record all six gates for the exact commit:
|
|
15
|
+
|
|
16
|
+
1. `pyproject.toml` and `src/icv_trace/__init__.py` contain the same version,
|
|
17
|
+
and `CHANGELOG.md` has a dated matching heading with nothing to ship under
|
|
18
|
+
Unreleased.
|
|
19
|
+
2. Every lint, mypy and Python 3.11 through 3.14 test leg in `ci.yml` is green.
|
|
20
|
+
3. In a clean virtual environment, reproduce the `publish.yml` install list
|
|
21
|
+
and pass its installed-wheel test command.
|
|
22
|
+
4. Build wheel and sdist, run `twine check`, inspect the wheel for
|
|
23
|
+
`icv_trace`, `core.py`, `adapters.py`, `human.py` and `py.typed`, then
|
|
24
|
+
install the wheel cleanly and import the documented package-root API. After
|
|
25
|
+
publication repeat the installation by name from the target index.
|
|
26
|
+
5. Review the tagged commit's own `publish.yml`.
|
|
27
|
+
6. State all existing-consumer behaviour in the release notes. For `0.1.0rc3`,
|
|
28
|
+
the human renderer is opt-in and core record semantics remain unchanged.
|
|
29
|
+
Logging, Eliot and OpenTelemetry helpers remain experimental and unsupported.
|
|
30
|
+
|
|
31
|
+
## Tag
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
git checkout main
|
|
35
|
+
git pull
|
|
36
|
+
git tag v<version>
|
|
37
|
+
git push origin v<version>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Confirm the workflow publishes to PyPI, creates the appropriate release
|
|
41
|
+
record, and that the package installs by name. Do not move a tag. If any gate
|
|
42
|
+
or publish step fails, fix it on a new commit and publish a new release-
|
|
43
|
+
candidate version.
|
|
44
|
+
|
|
45
|
+
## Public PyPI release and private-index transition
|
|
46
|
+
|
|
47
|
+
The repository is public and its trusted publisher is restricted to
|
|
48
|
+
`icvoss/icv-trace`, workflow `publish.yml`, environment `pypi`. Run every gate
|
|
49
|
+
above against the exact commit whose workflow targets PyPI, including a clean
|
|
50
|
+
PyPI installation after publication.
|
|
51
|
+
|
|
52
|
+
The first public version is a deliberate compatibility point. Release notes
|
|
53
|
+
must state the upgrade path for private-index consumers: pin the version while
|
|
54
|
+
validating the PyPI artefact, remove the private extra index only after all
|
|
55
|
+
private dependencies are absent, and retain the private index where other
|
|
56
|
+
private packages still require it. Do not replace an existing tag or silently
|
|
57
|
+
move consumers across indexes. Record the target index, installed artefact
|
|
58
|
+
version and consumer-visible behaviour in the release evidence.
|