vectorstep-service-mcp 0.1.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.
Files changed (33) hide show
  1. vectorstep_service_mcp-0.1.0/LICENSE +21 -0
  2. vectorstep_service_mcp-0.1.0/PKG-INFO +84 -0
  3. vectorstep_service_mcp-0.1.0/README.md +53 -0
  4. vectorstep_service_mcp-0.1.0/pyproject.toml +55 -0
  5. vectorstep_service_mcp-0.1.0/setup.cfg +4 -0
  6. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/__init__.py +1 -0
  7. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/__main__.py +13 -0
  8. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/client.py +59 -0
  9. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/docs/__init__.py +0 -0
  10. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/docs/confidence_and_trust_vector.md +120 -0
  11. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/docs/promotion_readiness.md +97 -0
  12. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/docs/prompt_versions.md +110 -0
  13. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/docs/stages_and_scoping.md +58 -0
  14. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/docs/statuses_and_accuracy.md +75 -0
  15. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/errors.py +75 -0
  16. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/server.py +9 -0
  17. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/tools/__init__.py +1 -0
  18. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/tools/analytics.py +283 -0
  19. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/tools/docs.py +78 -0
  20. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/tools/read.py +91 -0
  21. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp/tools/write.py +149 -0
  22. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp.egg-info/PKG-INFO +84 -0
  23. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp.egg-info/SOURCES.txt +31 -0
  24. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp.egg-info/dependency_links.txt +1 -0
  25. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp.egg-info/entry_points.txt +2 -0
  26. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp.egg-info/requires.txt +7 -0
  27. vectorstep_service_mcp-0.1.0/src/vectorstep_service_mcp.egg-info/top_level.txt +1 -0
  28. vectorstep_service_mcp-0.1.0/tests/test_client.py +118 -0
  29. vectorstep_service_mcp-0.1.0/tests/test_e2e.py +155 -0
  30. vectorstep_service_mcp-0.1.0/tests/test_stub.py +5 -0
  31. vectorstep_service_mcp-0.1.0/tests/test_tools_docs.py +112 -0
  32. vectorstep_service_mcp-0.1.0/tests/test_tools_read.py +435 -0
  33. vectorstep_service_mcp-0.1.0/tests/test_tools_write.py +288 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alex Dalton
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,84 @@
1
+ Metadata-Version: 2.4
2
+ Name: vectorstep-service-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server exposing VectorStep's authoring, inspection, and analytics surface to MCP clients (Claude Code/Desktop).
5
+ Author-email: Alex Dalton <alex@vectorstep.io>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://vectorstep.io
8
+ Project-URL: Documentation, https://vectorstep.io/docs/integrations/mcp/
9
+ Project-URL: Changelog, https://vectorstep.io/docs/about/status-and-support/
10
+ Keywords: mcp,vectorstep,ai,agents,orchestration,pipelines
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Libraries
20
+ Classifier: Topic :: System :: Monitoring
21
+ Requires-Python: >=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: mcp==1.28.1
25
+ Requires-Dist: httpx>=0.27
26
+ Requires-Dist: pyyaml>=6.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8.0; extra == "dev"
29
+ Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # VectorStep Service MCP
33
+
34
+ An [MCP](https://modelcontextprotocol.io) server that exposes [VectorStep](https://github.com/bantex01/VectorStep) — pipeline authoring, run inspection, and analytics — to MCP clients such as Claude Code and Claude Desktop.
35
+
36
+ ## What it is
37
+
38
+ A separate, standalone repository and process with no import-level dependency on VectorStep — the two are coupled only over HTTP, so each can be developed, versioned, and deployed independently.
39
+
40
+ It lets an MCP client create and edit pipelines and step-library definitions (with the same validation VectorStep itself uses), inspect runs and steps, answer operational and quality questions, and trigger runs or submit human feedback. Agents are not authored here — that's the job of the companion `VectorStep-Gateway-MCP`; this server may only read agents, to help author pipelines that reference them.
41
+
42
+ ## Quick start
43
+
44
+ ```bash
45
+ python3 -m venv .venv
46
+ .venv/bin/pip install -e .
47
+ ```
48
+
49
+ CI runs this repo's test suite on every push and pull request
50
+ (`.github/workflows/tests.yml`).
51
+
52
+ Then register it with your MCP client, pointing at a running VectorStep instance:
53
+
54
+ ```bash
55
+ claude mcp add vectorstep-service \
56
+ --env VECTORSTEP_BASE_URL=http://127.0.0.1:8000 \
57
+ -- /absolute/path/to/VectorStep-Service-MCP/.venv/bin/python -m vectorstep_service_mcp
58
+ ```
59
+
60
+ Full install, client config (Claude Code, Claude Desktop, MCP Inspector), and tool inventory: [MCP servers](https://vectorstep.io/docs/integrations/mcp/).
61
+
62
+ ## Documentation
63
+
64
+ Full docs at [vectorstep.io](https://vectorstep.io/docs/):
65
+
66
+ | Section | Covers |
67
+ |---|---|
68
+ | [MCP servers](https://vectorstep.io/docs/integrations/mcp/) | Install, client config, tool inventory, `explain` tool, write-path design notes for both MCP servers |
69
+ | [Pipelines](https://vectorstep.io/docs/pipelines/schema/) | The pipeline/step schema this server authors against |
70
+
71
+ ## The ecosystem
72
+
73
+ | Repo | Role |
74
+ |---|---|
75
+ | **VectorStep** | The orchestration service: webhook intake, pipeline runner, trust gating, UI, analytics |
76
+ | **VectorStep-Gateway** | WebSocket gateway that runs agents: LLM providers, MCP tools, the full agentic loop |
77
+ | **VectorStep-Service-MCP** | MCP server exposing pipeline authoring, run inspection and analytics to Claude Code/Desktop |
78
+ | **VectorStep-Gateway-MCP** | MCP server for authoring and inspecting Gateway agents |
79
+
80
+ ## Licence
81
+
82
+ VectorStep Service MCP is proprietary software, free to download and use under the terms in [`LICENSE`](LICENSE). The source is not publicly distributed and this repository is private.
83
+
84
+ Bug reports, questions, and feature requests are welcome at **alex@vectorstep.io**. Code contributions are not accepted — see clause 6 of the licence. For a suspected vulnerability, follow [`SECURITY.md`](SECURITY.md) rather than emailing the address above.
@@ -0,0 +1,53 @@
1
+ # VectorStep Service MCP
2
+
3
+ An [MCP](https://modelcontextprotocol.io) server that exposes [VectorStep](https://github.com/bantex01/VectorStep) — pipeline authoring, run inspection, and analytics — to MCP clients such as Claude Code and Claude Desktop.
4
+
5
+ ## What it is
6
+
7
+ A separate, standalone repository and process with no import-level dependency on VectorStep — the two are coupled only over HTTP, so each can be developed, versioned, and deployed independently.
8
+
9
+ It lets an MCP client create and edit pipelines and step-library definitions (with the same validation VectorStep itself uses), inspect runs and steps, answer operational and quality questions, and trigger runs or submit human feedback. Agents are not authored here — that's the job of the companion `VectorStep-Gateway-MCP`; this server may only read agents, to help author pipelines that reference them.
10
+
11
+ ## Quick start
12
+
13
+ ```bash
14
+ python3 -m venv .venv
15
+ .venv/bin/pip install -e .
16
+ ```
17
+
18
+ CI runs this repo's test suite on every push and pull request
19
+ (`.github/workflows/tests.yml`).
20
+
21
+ Then register it with your MCP client, pointing at a running VectorStep instance:
22
+
23
+ ```bash
24
+ claude mcp add vectorstep-service \
25
+ --env VECTORSTEP_BASE_URL=http://127.0.0.1:8000 \
26
+ -- /absolute/path/to/VectorStep-Service-MCP/.venv/bin/python -m vectorstep_service_mcp
27
+ ```
28
+
29
+ Full install, client config (Claude Code, Claude Desktop, MCP Inspector), and tool inventory: [MCP servers](https://vectorstep.io/docs/integrations/mcp/).
30
+
31
+ ## Documentation
32
+
33
+ Full docs at [vectorstep.io](https://vectorstep.io/docs/):
34
+
35
+ | Section | Covers |
36
+ |---|---|
37
+ | [MCP servers](https://vectorstep.io/docs/integrations/mcp/) | Install, client config, tool inventory, `explain` tool, write-path design notes for both MCP servers |
38
+ | [Pipelines](https://vectorstep.io/docs/pipelines/schema/) | The pipeline/step schema this server authors against |
39
+
40
+ ## The ecosystem
41
+
42
+ | Repo | Role |
43
+ |---|---|
44
+ | **VectorStep** | The orchestration service: webhook intake, pipeline runner, trust gating, UI, analytics |
45
+ | **VectorStep-Gateway** | WebSocket gateway that runs agents: LLM providers, MCP tools, the full agentic loop |
46
+ | **VectorStep-Service-MCP** | MCP server exposing pipeline authoring, run inspection and analytics to Claude Code/Desktop |
47
+ | **VectorStep-Gateway-MCP** | MCP server for authoring and inspecting Gateway agents |
48
+
49
+ ## Licence
50
+
51
+ VectorStep Service MCP is proprietary software, free to download and use under the terms in [`LICENSE`](LICENSE). The source is not publicly distributed and this repository is private.
52
+
53
+ Bug reports, questions, and feature requests are welcome at **alex@vectorstep.io**. Code contributions are not accepted — see clause 6 of the licence. For a suspected vulnerability, follow [`SECURITY.md`](SECURITY.md) rather than emailing the address above.
@@ -0,0 +1,55 @@
1
+ [project]
2
+ name = "vectorstep-service-mcp"
3
+ version = "0.1.0"
4
+ description = "MCP server exposing VectorStep's authoring, inspection, and analytics surface to MCP clients (Claude Code/Desktop)."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "Alex Dalton", email = "alex@vectorstep.io" }]
10
+ keywords = ["mcp", "vectorstep", "ai", "agents", "orchestration", "pipelines"]
11
+ classifiers = [
12
+ "Development Status :: 4 - Beta",
13
+ "Intended Audience :: Developers",
14
+ "Intended Audience :: System Administrators",
15
+ "Operating System :: OS Independent",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Topic :: Software Development :: Libraries",
21
+ "Topic :: System :: Monitoring",
22
+ ]
23
+ dependencies = [
24
+ "mcp==1.28.1",
25
+ "httpx>=0.27",
26
+ "pyyaml>=6.0",
27
+ ]
28
+
29
+ [project.optional-dependencies]
30
+ dev = [
31
+ "pytest>=8.0",
32
+ "pytest-asyncio>=0.24",
33
+ ]
34
+
35
+ [project.urls]
36
+ Homepage = "https://vectorstep.io"
37
+ Documentation = "https://vectorstep.io/docs/integrations/mcp/"
38
+ Changelog = "https://vectorstep.io/docs/about/status-and-support/"
39
+
40
+ [project.scripts]
41
+ vectorstep-service-mcp = "vectorstep_service_mcp.__main__:main"
42
+
43
+ [build-system]
44
+ requires = ["setuptools>=77"]
45
+ build-backend = "setuptools.build_meta"
46
+
47
+ [tool.setuptools.packages.find]
48
+ where = ["src"]
49
+
50
+ [tool.setuptools.package-data]
51
+ vectorstep_service_mcp = ["docs/*.md"]
52
+
53
+ [tool.pytest.ini_options]
54
+ asyncio_mode = "auto"
55
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,13 @@
1
+ """Entry point for the VectorStep Service MCP server. Runs over stdio (Claude Code /
2
+ Desktop) — see the README for VECTORSTEP_BASE_URL / VECTORSTEP_WEBHOOK_TOKEN configuration."""
3
+
4
+ from . import tools # noqa: F401 — importing registers every @mcp.tool()
5
+ from .server import mcp
6
+
7
+
8
+ def main() -> None:
9
+ mcp.run(transport="stdio")
10
+
11
+
12
+ if __name__ == "__main__":
13
+ main()
@@ -0,0 +1,59 @@
1
+ """Thin HTTP client for talking to a VectorStep service instance.
2
+
3
+ This is the ONLY coupling to VectorStep (SPEC-vectorstep-service-mcp.md §2.3): no VectorStep
4
+ code is imported anywhere in this package. Configured from VECTORSTEP_BASE_URL /
5
+ VECTORSTEP_WEBHOOK_TOKEN — see the README.
6
+ """
7
+
8
+ import os
9
+
10
+ import httpx
11
+
12
+ from .errors import VectorStepAPIError, error_from_response
13
+
14
+
15
+ class VectorStepClient:
16
+ def __init__(
17
+ self,
18
+ base_url: str | None = None,
19
+ token: str | None = None,
20
+ timeout: float = 30.0,
21
+ transport: httpx.AsyncBaseTransport | None = None,
22
+ ):
23
+ self.base_url = (base_url or os.environ.get("VECTORSTEP_BASE_URL") or "http://127.0.0.1:8000").rstrip("/")
24
+ self.token = token if token is not None else os.environ.get("VECTORSTEP_WEBHOOK_TOKEN")
25
+ self.timeout = timeout
26
+ # Injectable for tests (httpx.MockTransport) — None uses the real network.
27
+ self.transport = transport
28
+
29
+ def _headers(self) -> dict:
30
+ return {"Authorization": f"Bearer {self.token}"} if self.token else {}
31
+
32
+ async def request(self, method: str, path: str, **kwargs) -> dict:
33
+ url = f"{self.base_url}{path}"
34
+ try:
35
+ async with httpx.AsyncClient(timeout=self.timeout, transport=self.transport) as http_client:
36
+ resp = await http_client.request(method, url, headers=self._headers(), **kwargs)
37
+ except httpx.RequestError as exc:
38
+ raise VectorStepAPIError(
39
+ "network", f"Could not reach VectorStep at {self.base_url}: {exc}",
40
+ ) from exc
41
+
42
+ if resp.status_code >= 400:
43
+ raise error_from_response(resp)
44
+
45
+ if not resp.content:
46
+ return {}
47
+ return resp.json()
48
+
49
+ async def get(self, path: str, params: dict | None = None) -> dict:
50
+ return await self.request("GET", path, params=params)
51
+
52
+ async def post(self, path: str, json: dict | None = None) -> dict:
53
+ return await self.request("POST", path, json=json)
54
+
55
+ async def put(self, path: str, json: dict | None = None) -> dict:
56
+ return await self.request("PUT", path, json=json)
57
+
58
+ async def delete(self, path: str) -> dict:
59
+ return await self.request("DELETE", path)
@@ -0,0 +1,120 @@
1
+ # Confidence and the trust vector
2
+
3
+ `get_run` returns several confidence-shaped fields per step. They are **not**
4
+ duplicates of the same number — each is a distinct, independent signal, and
5
+ VectorStep combines them by taking the *weakest* one, not an average. This doc
6
+ maps those fields to what they actually mean, so you don't report
7
+ `primary_confidence` as "VectorStep's confidence in this step" when it's really
8
+ just the agent's own unverified self-report.
9
+
10
+ ## The four signals (+ calibration)
11
+
12
+ | Signal | Field on a step | What it actually measures |
13
+ |---|---|---|
14
+ | **S** — self-report | `primary_confidence` | What the agent said about its own answer. The **least** trustworthy number here — it's the agent grading its own work, no outside check. |
15
+ | **V** — verifier | `verifier_confidence` (see also `verifier_mode`, `verifier_agent`, `verifier_model`) | A second agent's opinion, if a `verifier:` block was configured. `verifier_mode: "critic"` saw the primary's answer and reviewed it (its errors correlate with the primary's own — weaker signal); `"independent"` redid the task blind (uncorrelated — stronger signal). A verifier can only ever *lower* the combined score, never raise it. |
16
+ | **G** — grounding | `grounding_score` (0–1, or `null` if not computed) | Of the primary's specific factual claims, what fraction are actually backed by a real tool-call result in its own trace? This is about evidence, not correctness of reasoning. `null` means grounding wasn't configured for this step at all — that's different from a low score. |
17
+ | **D** — deterministic check | `deterministic_passed` (bool, or `null` if none declared) | A hard, computer-verified fact (a shell command, webhook call, or human approval) — no model involved. If declared and failed, it forces trust to **zero**, no matter how confident everything else was. `null` means no checks were declared, not that one passed. |
18
+ | **Calibration** | inside `trust_report.calibration` | Whether this exact agent/model's *real historical accuracy* at this confidence level (from past human-graded runs) should replace the raw self-report. The one signal that can move the score **up** as well as down. |
19
+
20
+ To see the full calibration picture for a step — every confidence bin, per
21
+ (agent, model, provider), not just the one bin a single run happened to land
22
+ in — call `get_step_calibration(step_name)` directly rather than piecing it
23
+ together by sampling `get_run` across many runs.
24
+
25
+ ### Calibration buckets are versioned
26
+
27
+ A calibration bucket is keyed by `(step_name, agent, model, provider,
28
+ prompt_hash, agent_version)`, not just the first four. Editing a step's
29
+ prompt template, or editing a Gateway agent's `agent.yaml`/`soul.md`, starts
30
+ a **new** bucket — outcomes from the old prompt/agent config never count as
31
+ evidence for the new one, and vice versa. This is deliberate: a bucket
32
+ mixing two different prompts isn't a bigger sample, it's a wrong one.
33
+
34
+ Consequences worth knowing before reporting on calibration data:
35
+
36
+ - **A small bucket isn't necessarily a new or unused step.** It may just be
37
+ mid-history under a recently-edited prompt or agent — call
38
+ `get_step_versions(step_name)` / `get_agent_versions(agent_name)` to see
39
+ the rest of that history before concluding a step "has no track record."
40
+ - **Reverting a prompt restores its history for free.** Because buckets are
41
+ keyed by content hash, going back to an exact previous template rejoins
42
+ that version's original bucket and all its labelled results — nothing is
43
+ lost.
44
+ - **`trust_report.calibration.bucket_reset`**, when present, tells you
45
+ explicitly that THIS run's bucket just reset and why:
46
+ `reason` is `"prompt_changed"`, `"agent_changed"`, or `"both_changed"`;
47
+ `previous_validated_n` is how many marked results the prior version had.
48
+ Treat this as the explanation for a step that suddenly stopped being
49
+ calibrated, not as a sign anything is broken.
50
+ - **`agent_version` changes originate in the Gateway repo, not VectorStep's own
51
+ YAML.** A pipeline/step YAML file can look completely unchanged while an
52
+ agent's `soul.md` was edited on the Gateway — if calibration resets for a
53
+ step you didn't touch, check `get_agent_versions` for the agent it uses
54
+ before assuming something is wrong on the VectorStep side.
55
+
56
+ ## `effective_confidence` vs. `trust_report.combined_trust`
57
+
58
+ `effective_confidence` on a step is the self-report *after* the verifier is
59
+ applied (S adjusted by V) — it predates grounding/deterministic/calibration
60
+ in the pipeline's history and is kept for backward compatibility.
61
+
62
+ `trust_report.combined_trust` (inside the JSON blob in `trust_report`) is the
63
+ **actual final number the step's gate compared against its
64
+ `confidence_threshold`** — after verifier, calibration, grounding, and
65
+ deterministic checks have all been applied in that order. **When you want
66
+ "how much did VectorStep actually trust this step's result," use
67
+ `trust_report.combined_trust`, not `effective_confidence`.**
68
+
69
+ ## Reading `trust_report`
70
+
71
+ ```json
72
+ {
73
+ "signals": {
74
+ "S": 0.95, "S_after_V": 0.95,
75
+ "V": 0.85, "V_mode": "critic", "V_combination_strategy": "veto", "V_veto_floor": 0.6,
76
+ "G": 0.50, "C": null, "D": true
77
+ },
78
+ "combined_trust": 0.50,
79
+ "grounding": { "...": "per-claim detail" },
80
+ "deterministic_checks": [{"name": "...", "passed": true, "...": "..."}],
81
+ "calibration": { "...": "bin/bucket detail, or null if not enforced" },
82
+ "gate": {
83
+ "policy": "trust_vector",
84
+ "confidence_threshold": 0.75,
85
+ "on_low_confidence": "escalate"
86
+ }
87
+ }
88
+ ```
89
+
90
+ Order the signals are applied, each stage only able to lower the number
91
+ (calibration is the sole exception):
92
+
93
+ ```
94
+ S → (verifier) → S_after_V → (calibration, if enforced+validated) →
95
+ → (grounding, if enforced — a ceiling) → (deterministic checks — force to
96
+ zero on any failure) → combined_trust → compared to confidence_threshold
97
+ ```
98
+
99
+ `trust_report.gate.policy` tells you whether any of this mattered at all:
100
+ `"legacy_confidence"` means the step has none of verifier/grounding/
101
+ deterministic/calibration configured — it's a plain self-report-vs-threshold
102
+ gate, and every signal above `S` will be `null`. `"trust_vector"` means at
103
+ least one of these mechanisms is active.
104
+
105
+ ## A field being `null` is information, not a gap
106
+
107
+ `verifier_confidence: null` → no verifier was configured for this step, not
108
+ "the verifier failed." `grounding_score: null` → grounding wasn't computed,
109
+ not "zero evidence." `deterministic_passed: null` → no hard checks were
110
+ declared, not "unknown pass/fail." Don't treat `null` as a bad result — it
111
+ means the mechanism was never turned on for this step.
112
+
113
+ ## What "escalated because of low confidence" actually means
114
+
115
+ If `trust_report.combined_trust` fell below `confidence_threshold`, the step
116
+ took whatever action `gate.on_low_confidence` says (`escalate` / `abort` /
117
+ `proceed`). A step escalating doesn't mean anything went wrong with the
118
+ agent's answer — it means the *evidence for trusting that answer* didn't
119
+ clear the bar the pipeline author set. See `statuses-and-accuracy` for why
120
+ that's often the *correct*, intended outcome rather than a failure.
@@ -0,0 +1,97 @@
1
+ # Promotion readiness — owner-defined criteria, not a hardcoded bar
2
+
3
+ `get_promotion_readiness(name)` reports whether a `stage: testing` pipeline's
4
+ steps already have the evidence their OWNER requires before flipping
5
+ `stage:` to `production`. `preview_promotion_readiness(name, readiness,
6
+ apply_to)` tests a candidate `readiness:` config against the same evidence
7
+ without writing anything. Both are **strictly advisory** — neither call
8
+ blocks, gates, or changes VectorStep's behaviour in any way. The `stage:` edit
9
+ itself stays a one-line YAML change a human makes and commits, exactly as
10
+ before this feature existed.
11
+
12
+ ## Four independent tiers
13
+
14
+ A pipeline (or a step within it) can configure any subset of four tiers in
15
+ its `readiness:` YAML block:
16
+
17
+ - **operational** — pure run-completion counting: N runs that ended in an
18
+ acceptable status. The cheapest bar, and the only one a non-LLM step
19
+ (`webhook`/`notify`/`human`/`pipeline` executor) can ever satisfy, since
20
+ those never write `effective_confidence`. Can only ever report `pass` or
21
+ `insufficient_data` — twenty completed runs plus five failed ones still
22
+ satisfies "twenty completed runs," so this tier never reports `fail`.
23
+ - **confidence** — mean self-reported `effective_confidence` over qualifying
24
+ runs. A weak signal alone (a model can be confidently wrong), useful as an
25
+ early checkpoint before anyone has marked anything.
26
+ - **accuracy** — judged accuracy (correct=1.0, partial=0.5, incorrect=0.0)
27
+ over human/deterministic/run-level labels, using the same label-precedence
28
+ chain as calibration: a human mark beats a failed deterministic check beats
29
+ an inherited run-level rating.
30
+ - **calibration** — the strongest bar: not just "the output is good" but "the
31
+ step's confidence NUMBER can be trusted," using the same bucket machinery
32
+ as `get_step_calibration`.
33
+
34
+ Every CONFIGURED tier must show `pass` for a step to read `ready`. An
35
+ unconfigured tier is not a failure — it's simply not asked. A pipeline can
36
+ require `operational` + `accuracy` without touching `calibration`, because
37
+ those answer genuinely different questions.
38
+
39
+ ## Verdict vocabulary
40
+
41
+ Per tier: `not_configured | insufficient_data | pass | fail`.
42
+
43
+ **`insufficient_data` means "the bar can still be met by accumulating more
44
+ evidence." `fail` means "observed evidence actively contradicts the bar."**
45
+ These are not interchangeable when summarising a step's readiness for a
46
+ user — a step reading `insufficient_data` just needs more runs/marks; a step
47
+ reading `fail` has a real problem right now.
48
+
49
+ Step-level and pipeline-level roll-ups use the same precedence:
50
+ `fail > insufficient_data > pass > not_configured`, surfacing as
51
+ `not_ready | building | no_data | ready | not_configured`.
52
+
53
+ ## THE trap: n_min is per confidence band, not a total
54
+
55
+ `calibration.n_min` (default 20) is the number of marked results needed **at
56
+ the same predicted-confidence band**, not a total across all bands. A step
57
+ with 100 marked results spread evenly across 10 confidence bands has only 10
58
+ in each band, and will **not** validate at `n_min: 20` even though 100
59
+ sounds like plenty. Always check the fullest band's own count
60
+ (`bins[i].n` on the relevant combo), never `total_n`, before concluding a
61
+ step is close to validating calibration.
62
+
63
+ ## `require_current_config` and `require_own_evidence`
64
+
65
+ Most tiers default `require_current_config: true` (except `operational`,
66
+ which defaults `false`) — evidence is filtered to runs matching the
67
+ pipeline's CURRENT prompt template and the step's most recently observed
68
+ `agent_version`. A step whose prompt was just edited can drop straight to
69
+ `insufficient_data` even with a long track record; `current_config` on each
70
+ step's result reports `prompt_hash_matches_history` and an explicit `notes`
71
+ entry naming how many earlier marked results were excluded, so this is never
72
+ a silent zero.
73
+
74
+ `calibration.require_own_evidence` (default `false`) lets a shared library
75
+ step's PRODUCTION track record from a *different* pipeline count, when the
76
+ agent/model/prompt/agent-version all match exactly — `production_pipelines`
77
+ on the calibration combo names which pipeline(s) contributed that evidence.
78
+ Set it `true` to restrict a step to only this pipeline's own runs.
79
+
80
+ ## Reading a result without asserted criteria
81
+
82
+ A pipeline with no `readiness:` block anywhere returns
83
+ `criteria_source: "none"` and asserts no verdict — but each step's
84
+ `observed_combos` still carries calibration bins evaluated at
85
+ `bin_width`/`n_min` (the tool's own parameters, default 0.1/20), so real
86
+ signal never disappears behind "no criteria configured." A step's own
87
+ configured calibration tier always uses its own values instead of these
88
+ defaults.
89
+
90
+ ## Preferring the ready-made narrative
91
+
92
+ Each step's result includes a `narrative`: a numbers-first, plain-English
93
+ walkthrough of every configured tier's verdict. Prefer quoting it directly
94
+ over re-deriving your own summary from the raw tier numbers — it already
95
+ accounts for the per-band n_min trap, provenance breakdowns, and the
96
+ `acceptable_statuses` "adding a status makes the bar LAXER, not stricter"
97
+ gotcha.
@@ -0,0 +1,110 @@
1
+ # Prompt and agent versioning
2
+
3
+ Every calibration bucket VectorStep computes is scoped to the exact configuration
4
+ that produced it — not just `(step_name, agent, model, provider)`, but also
5
+ the step's `prompt_hash` and the Gateway agent's `agent_version`. This doc
6
+ explains why, and how to read the tools that expose it
7
+ (`get_step_versions`, `get_agent_versions`) without misreading a healthy
8
+ reset as a problem.
9
+
10
+ ## The bug this exists to fix
11
+
12
+ Before this existed, editing a step's prompt template — or editing a
13
+ Gateway agent's `soul.md`/`agent.yaml` — silently kept counting outcomes
14
+ from the OLD configuration as evidence for the NEW one. For a step with
15
+ `calibration: {enforce: true}`, that meant the enforcement gate was making
16
+ real control decisions using a measured accuracy figure that described a
17
+ configuration that no longer existed. Nothing in the data indicated this
18
+ was happening — it was invisible.
19
+
20
+ Now, `prompt_hash` (a content hash of the step's prompt template) and
21
+ `agent_version` (a content hash of the agent's full config, computed by the
22
+ Gateway) are part of every calibration bucket's key. An edit to either one
23
+ starts a fresh bucket. Old labelled results stop counting toward the new
24
+ configuration.
25
+
26
+ ## A small bucket is not the same as a bad one
27
+
28
+ The bucket key growing from 4 components to 6 means buckets are smaller and
29
+ take longer to reach the labelled-sample minimum (`n_min`) than before this
30
+ existed. That is the honest cost of measuring the right thing — a bucket
31
+ that blends two different prompts is not a bigger sample, it's a wrong one.
32
+
33
+ Practically: if `get_step_calibration` shows a step with very few labelled
34
+ samples even though you know it's been running for months, don't conclude
35
+ it's under-used or unlabelled. Call `get_step_versions(step_name)` — it's
36
+ very likely mid-history under a prompt version that was edited recently,
37
+ and the rest of its track record is sitting under a different `prompt_hash`
38
+ in that same response (or under the synthetic legacy entry described next,
39
+ if the step predates this feature entirely).
40
+
41
+ ## The `prompt_hash: null` / `agent_version: null` legacy entry
42
+
43
+ `get_step_versions`/`get_agent_versions` can each return one entry whose
44
+ `prompt_hash`/`agent_version` is `null`. This is NOT an error and NOT a step
45
+ with no history — it represents every run recorded **before** prompt/agent
46
+ versioning existed, or before an operator ran the backfill script
47
+ (`backfill_prompt_versions.py`) for that step. `NULL` is a real, distinct
48
+ bucket in `get_step_calibration` too (never a wildcard — a `NULL` row never
49
+ pools with a real-hash row), so this entry's `runs_total`/`labelled_n` are
50
+ just as real as any other version's.
51
+
52
+ What's different about it: `template` (or `soul_md`/`agent_yaml`) is always
53
+ `null` for this entry — no text was ever captured for pre-versioning runs,
54
+ so there's nothing to show. `diff_from_previous` is `null` for it, and also
55
+ `null` for whichever real version comes right after it chronologically
56
+ (nothing to diff against a version with no text). Treat it exactly like a
57
+ `note`d agent snapshot: an honest gap, not evidence anything is broken.
58
+ Running the backfill script resolves it into a real, named version (on the
59
+ operator's assertion that the current on-disk prompt/agent config matches
60
+ what those historical runs actually used) — but until that happens, expect
61
+ to see it on nearly every step/agent that predates this feature.
62
+
63
+ ## Reverting a prompt restores its history — for free
64
+
65
+ The prompt-version registry is content-addressed: the hash IS the primary
66
+ key. If an operator edits a prompt, doesn't like the result, and reverts it
67
+ back to the exact original text, that revert reproduces the exact same
68
+ hash as before — and automatically rejoins that version's original
69
+ calibration history. No special-casing, no data loss. This is intentional,
70
+ not a quirk to route around.
71
+
72
+ The corollary: a *reformatting* of a prompt (different whitespace,
73
+ reindentation, an added blank line) genuinely produces a different hash and
74
+ a fresh bucket — normalisation only strips trailing whitespace per line and
75
+ leading/trailing blank lines, nothing else. Blank-line structure and
76
+ indentation can genuinely affect model behaviour, so treating a
77
+ reformatted prompt as unchanged would be the wrong call more often than
78
+ the current conservative default.
79
+
80
+ ## `agent_version` changes happen in a different repo
81
+
82
+ `prompt_hash` is something VectorStep computes itself, from a step's
83
+ `prompt_template` — the text is always right there in `get_step_versions`'
84
+ response. `agent_version` is different: it's a hash the **Gateway**
85
+ computes over an agent's entire config, including `soul.md`, and VectorStep
86
+ cannot recompute it or predict when it will change. A pipeline/step YAML
87
+ file can be completely untouched while an agent's `soul.md` is edited on
88
+ the Gateway side — and that alone is enough to reset calibration for every
89
+ step using that agent.
90
+
91
+ If a step's calibration resets and you didn't edit anything in VectorStep:
92
+ 1. Check `trust_report.calibration.bucket_reset.reason` on a recent run of
93
+ that step — `"agent_changed"` or `"both_changed"` points at the Gateway,
94
+ not the step's own prompt.
95
+ 2. Call `get_agent_versions(agent_name)` (bare name, no `gateway:` prefix)
96
+ to see the agent's version history and what changed.
97
+ 3. A version's `note` field (instead of recovered `soul_md`/`agent_yaml`
98
+ text) means VectorStep couldn't confirm that snapshot at the time — the
99
+ Gateway was unreachable, or the agent changed again before VectorStep could
100
+ ask. That's an honest gap in VectorStep's records, not evidence the agent
101
+ itself has no configuration.
102
+
103
+ ## Quick reference
104
+
105
+ | Question | Tool |
106
+ |---|---|
107
+ | Is this step's confidence trustworthy right now? | `get_step_calibration(step_name)` |
108
+ | Did a specific prompt edit actually help? | `get_step_versions(step_name)` |
109
+ | What changed in this agent, and which steps did it affect? | `get_agent_versions(agent_name)` |
110
+ | Why did this run's bucket reset? | `get_run(run_id)` → `trust_report.calibration.bucket_reset` |