Skip to content

Commit 970d673

Browse files
author
dalqen-agent
committed
dalqen snapshot: implement attempt-1
1 parent c721b3d commit 970d673

4 files changed

Lines changed: 169 additions & 1 deletion

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@ python -m build \
2525

2626
仓库结构为后续独立 Python runtime modules 预留位置,但不会为了目录对称创建空包。
2727
安装 `pole-client-python` 不会自动导入 `sitecustomize`,也不会修改用户进程中的框架行为。
28+
29+
后续 `pole-instrument` 启动器的 Proposed 设计见
30+
[`context-kg/technical/adr/pole-instrument.md`](context-kg/technical/adr/pole-instrument.md);当前没有
31+
已获批准的框架适配器或生产实现。

context-kg/_meta/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: pole-python Context-KG
33
tags: [meta, index]
44
links: []
5-
updated: 2026-08-12
5+
updated: 2026-08-18
66
sources: 0
77
---
88

@@ -11,6 +11,7 @@ sources: 0
1111
## Technical
1212

1313
- [[pole-python-monorepo]] — 轻量 Monorepo 与兼容发行边界 | architecture, python, monorepo, packaging
14+
- [[pole-instrument]] — Proposed instrumentation launcher contract | architecture, python, instrumentation
1415

1516
## Tasks
1617

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
title: pole-instrument Technical Design
3+
tags: [architecture, python, instrumentation, launcher]
4+
links: [pole-python-monorepo, todo]
5+
updated: 2026-08-18
6+
sources: 4
7+
---
8+
9+
# pole-instrument Technical Design
10+
11+
Status: Proposed. This document fixes the product boundary and runtime contracts that an implementation must satisfy. Shipping remains blocked on the open framework and failure-default decisions below.
12+
13+
## Scope and packaging boundary
14+
15+
`pole-instrument` is a future executable supplied by a separate `pole-instrument` distribution. Its implementation modules live under `pole_instrument` and depend on, but are not imported by, `pole-client-python`. The existing `pole_client` import, exports, version, and explicit-use behavior do not change. Installing or importing `pole_client` never activates instrumentation.
16+
17+
## Activation contract
18+
19+
The proposed command is `pole-instrument [launcher options] -- application [arguments...]`. The separator is mandatory. Argument forwarding preserves every application argument at the Python string boundary and does not log it. Environment variables are inherited except for explicit launcher configuration. Startup validates configuration, loads one selected adapter, installs it, and starts the application. Shutdown unpatches and closes launcher resources. Signals and the application's exit status are preserved. Exit codes are `2` for launcher syntax or validation and `70` for configured fail-closed activation failure.
20+
21+
### Adapter interface
22+
23+
An adapter exposes `install(runtime) -> handle`; the handle exposes idempotent `unpatch()`. The Adapter boundary applies outbound target metadata and TrafficContext baggage only at documented request hooks. No concrete framework adapter is approved or advertised.
24+
25+
## Existing SDK integration
26+
27+
The launcher creates at most one `SidecarSession` per live process and never duplicates UDS logic. Adapters request current endpoints, construct `TargetService` metadata, and use `TrafficContext` extraction, scope, and injection. A disconnect invalidates availability immediately; no stale endpoint may be cached or reused.
28+
29+
## Failure policy
30+
31+
The implementation exposes `fail-open` and `fail-closed`; the default remains an Open question.
32+
33+
| Failure | Fail-open | Fail-closed |
34+
| --- | --- | --- |
35+
| Launcher setup or validation | exit `2` | exit `2` |
36+
| Sidecar unavailable | warn and run uninstrumented | error and exit `70` |
37+
| Invalid listener snapshot | discard it, warn, run without route | error and exit `70` |
38+
| Adapter import or patch | roll back partial patch and continue | roll back and exit `70` |
39+
| Runtime callback | contain, warn, call original path | error and terminate only at a safe boundary |
40+
| Sidecar disconnect | invalidate routes and continue | invalidate routes and request controlled termination |
41+
42+
## Patch lifecycle and Import order
43+
44+
Patching occurs after validation and before application import. Activation is idempotent for the same adapter/configuration; conflicts fail. Every patch registers its inverse, and partial patch failure rolls back in reverse order. Shutdown and test isolation call `unpatch()`; repeated cleanup is harmless. Library imports have no patch side effect.
45+
46+
## Process model
47+
48+
An `exec` requires activation in the replacement program. A subprocess is instrumented only when it opts into the launcher. A pre-fork parent may validate and import adapter code but owns no active thread or gRPC channel. Each post-fork child installs hooks and creates a fresh SidecarSession. Fork after activation is unsupported: the child disables inherited handles and emits a stable diagnostic until explicit reactivation.
49+
50+
## Configuration
51+
52+
Precedence is `CLI > environment > default`.
53+
54+
| Meaning | CLI | Environment | Default / validation |
55+
| --- | --- | --- | --- |
56+
| Adapter | `--adapter` | `POLE_INSTRUMENT_ADAPTER` | required registered name |
57+
| Failure mode | `--failure-mode` | `POLE_INSTRUMENT_FAILURE_MODE` | unresolved; two fixed values |
58+
| Socket | `--sidecar-socket` | `POLE_SIDECAR_SOCKET` | `resolve_sidecar_socket()` semantics |
59+
| Level | `--log-level` | `POLE_INSTRUMENT_LOG_LEVEL` | `warning`; fixed levels |
60+
61+
Empty explicit values fail validation. Secrets, credentials, metadata, application arguments, and baggage values are not logged.
62+
63+
## Diagnostics
64+
65+
Diagnostics are bounded structured records on stderr with timestamp, severity, stable diagnostic ID, component, and redacted message. Categories cover activation, unsupported adapters, Sidecar availability, snapshots, patch failure, callbacks, fork misuse, and shutdown. Records redact secrets and carrier/baggage data and are rate-limited by ID.
66+
67+
## Compatibility and artifacts
68+
69+
The baseline is Python >=3.9. Optional dependencies are isolated by adapter extras or distributions and are not eagerly imported. A future wheel/sdist contains `pole_instrument` and its entry point. Existing `pole-client-python` artifacts still exclude `sitecustomize.py`, `usercustomize.py`, `.pth`, the launcher, adapter dependencies, and implicit patches.
70+
71+
## Verification matrix
72+
73+
| Contract | Required automated evidence before release |
74+
| --- | --- |
75+
| Launcher | argv, environment, streams, signals, and exit codes |
76+
| Failure policy | every failure in both modes |
77+
| Sidecar disconnect | real UDS test proving no stale endpoint reuse |
78+
| Idempotency | duplicate activation, rollback, repeated unpatch |
79+
| Propagation | public Adapter boundary target and baggage test |
80+
| Fork | pre-fork/post-fork, spawn, subprocess, exec fresh-process tests |
81+
| Diagnostics | IDs, severity, rate limiting, redaction |
82+
| Package isolation | wheel/sdist and clean-environment tests |
83+
| Compatibility | every advertised Python/framework pair |
84+
85+
## Non-goals
86+
87+
- Selecting framework-specific support.
88+
- automatic sitecustomize activation or `.pth` activation.
89+
- changes to pole_client public behavior.
90+
- Instrumenting subprocesses without explicit opt-in.
91+
92+
## Open questions
93+
94+
- Which frameworks and versions are approved first?
95+
- Is the default fail-open or fail-closed?
96+
- Which server integrations offer a reliable post-fork callback?
97+
- Is a public activation API needed besides the executable?
98+
99+
No production implementation ships until these are accepted and executable compatibility evidence exists.
100+
101+
## Related pages
102+
103+
- [[pole-python-monorepo]]
104+
- [[todo]]
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import unittest
2+
from pathlib import Path
3+
4+
5+
ROOT = Path(__file__).resolve().parents[1]
6+
DESIGN = ROOT / "context-kg" / "technical" / "adr" / "pole-instrument.md"
7+
8+
9+
class PoleInstrumentDesignTest(unittest.TestCase):
10+
@classmethod
11+
def setUpClass(cls):
12+
cls.text = DESIGN.read_text(encoding="utf-8")
13+
14+
def assert_has(self, *phrases):
15+
for phrase in phrases:
16+
with self.subTest(phrase=phrase):
17+
self.assertIn(phrase, self.text)
18+
19+
def test_ac01_design_is_authoritative_and_discoverable(self):
20+
self.assert_has("# pole-instrument Technical Design", "Status: Proposed", "Scope")
21+
self.assertIn("[[pole-instrument]]", (ROOT / "context-kg" / "_meta" / "index.md").read_text())
22+
self.assertIn("pole-instrument", (ROOT / "README.md").read_text())
23+
24+
def test_ac02_packaging_boundary(self):
25+
self.assert_has("pole-instrument", "pole_instrument", "pole-client-python", "pole_client")
26+
27+
def test_ac03_runtime_contract_and_adapter_scope(self):
28+
self.assert_has("Activation contract", "Argument forwarding", "Exit codes", "Adapter interface", "No concrete framework adapter")
29+
30+
def test_ac04_existing_sdk_integration(self):
31+
self.assert_has("SidecarSession", "TargetService", "TrafficContext", "Adapter boundary")
32+
33+
def test_ac05_failure_policy(self):
34+
self.assert_has("Failure policy", "Launcher setup", "Sidecar unavailable", "Invalid listener snapshot", "Adapter import or patch", "Runtime callback", "stale endpoint")
35+
36+
def test_ac06_patch_lifecycle(self):
37+
self.assert_has("Patch lifecycle", "idempotent", "partial patch", "Import order", "unpatch")
38+
39+
def test_ac07_process_models(self):
40+
self.assert_has("Process model", "subprocess", "exec", "pre-fork", "post-fork", "fresh SidecarSession")
41+
42+
def test_ac08_configuration(self):
43+
self.assert_has("Configuration", "CLI > environment > default", "POLE_SIDECAR_SOCKET", "validation", "Secrets")
44+
45+
def test_ac09_diagnostics(self):
46+
self.assert_has("Diagnostics", "stderr", "severity", "diagnostic ID", "redact", "baggage")
47+
48+
def test_ac10_compatibility_and_artifacts(self):
49+
self.assert_has("Python >=3.9", "Optional dependencies", "sitecustomize.py", "usercustomize.py", ".pth")
50+
51+
def test_ac11_verification_matrix(self):
52+
self.assert_has("Verification matrix", "Launcher", "Sidecar disconnect", "Idempotency", "Fork", "Package isolation")
53+
54+
def test_ac12_non_goals_and_open_questions(self):
55+
self.assert_has("Non-goals", "Open questions", "framework-specific", "automatic sitecustomize activation", "changes to pole_client")
56+
57+
58+
if __name__ == "__main__":
59+
unittest.main()

0 commit comments

Comments
 (0)