Skip to content

Commit 2bba96d

Browse files
authored
Add kya governance skill and optional governance break-signal (#4)
Add kya governance skill and optional governance break-signal
2 parents 9e54e97 + 98f3ffb commit 2bba96d

9 files changed

Lines changed: 393 additions & 13 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ The list below is kept in alphabetical order by skill name.
1313
| Skill | Description |
1414
| --- | --- |
1515
| [`duct`](./skills/duct/SKILL.md) | Wrap any command with [con/duct](https://github.com/con/duct) to capture wall-clock time, CPU, and memory usage as structured logs, so agents and reviewers can inspect what a run actually consumed. |
16+
| [`kya`](./skills/kya/SKILL.md) | Govern and review agents with [veldt-kya](https://github.com/veldtlabs/veldt-kya) (Know Your Agents) — risk-score, consensus-judge, and drift-check an agent, emit compliance evidence, and write a governance verdict that the `labnb` loop can break on. |
1617
| [`labnb`](./skills/labnb/SKILL.md) | Create and maintain a concurrency-safe global lab notebook outside project roots, with startup summaries of related prior work, first-class idea capture and promotion, isolated experiment workspaces, focused companion skills, and append-only indexing across projects, investigations, and tasks. |
1718
| [`labnb-idea`](./skills/labnb-idea/SKILL.md) | Record a promising but not-yet-implemented experiment idea in the shared lab notebook index. |
1819
| [`labnb-promote`](./skills/labnb-promote/SKILL.md) | Promote a lab notebook idea into a concrete experiment with explicit budgets, source links, and provenance. |

skills/kya/SKILL.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
---
2+
name: kya
3+
description: Govern and review autonomous agents with veldt-kya (Know Your Agents) — risk-score an agent, run multi-judge consensus, detect configuration drift, and emit compliance evidence — then turn the result into a verdict that labnb's loop can act on. Use when a run needs a trust/authorization/policy check, not just a resource or metric check.
4+
---
5+
6+
# Know Your Agents (KYA) Governance Review
7+
8+
Use this skill to answer a different question from "is the run fast?" (see [`duct`](../duct/SKILL.md)) or "is the metric improving within budget?" (see [`labnb`](../labnb/SKILL.md)): **is the agent authorized, policy-compliant, and uncompromised?**
9+
10+
It wraps [`veldt-kya`](https://github.com/veldtlabs/veldt-kya) ("Know Your Agents", Apache-2.0), a governance SDK that risk-scores agents, runs consensus judges over their behavior, detects drift from an approved definition, and produces regulator-grade compliance evidence.
11+
12+
This is the third review axis in this repo: `duct` = resource accounting, `labnb` = budget / experimental-validity accounting, `kya` = trust / authorization / compliance accounting.
13+
14+
## When To Use
15+
16+
- Before granting an agent a privileged action (writing outside scope, executing SQL, calling external services), score or consensus-check it first.
17+
- When a long-running labnb experiment should stop if the agent drifts from its approved configuration or trips a policy judge — not only when the metric stalls.
18+
- When a run touches regulated or PII-bearing data and needs auditable authorization evidence (GDPR, HIPAA, EU AI Act, and similar).
19+
20+
Skip it for ordinary, low-stakes local iteration; governance has real cost (see Guardrails).
21+
22+
## Install
23+
24+
```bash
25+
pip install veldt-kya # Apache-2.0
26+
```
27+
28+
`veldt-kya` is a heavier, pre-1.0 dependency (database, identity bindings, optional external judges). Keep it **optional** — it is never required by the `labnb` core. Install it only in environments that need governance.
29+
30+
## Core Capabilities
31+
32+
```python
33+
from kya import score_agent, normalize_agent_def
34+
from kya import canonical_hash, detect_drift
35+
from kya.scorer_orchestrator import check_consensus, register_available_adapters
36+
from kya import compliance_summary
37+
```
38+
39+
- **Pre-deployment scoring**`score_agent(...)` returns an object with `score`, a severity `bucket` (e.g. `"critical"`), and per-factor `factors` (each with `.name` / `.delta`).
40+
- **Runtime consensus**`check_consensus(...)` runs multiple judges in parallel and returns `consensus` (`OK` / `SPLIT` / `UNCLEAR` / `BREACH`), `per_dimension` scores (input_safety, safety, faithfulness), and per-`judges` detail.
41+
- **Drift detection**`canonical_hash(definition)` plus `detect_drift(...)` flag unauthorized configuration changes against an approved baseline.
42+
- **Compliance evidence**`compliance_summary(...)` emits model cards / breach-notification artifacts across many regimes.
43+
44+
## Producing A labnb Governance Verdict
45+
46+
`labnb`'s `monitor_slice.py check` can break a slice on a **governance** signal, but it stays dependency-free: it reads a small JSON **verdict file** rather than importing KYA. This skill's job is to run a governance check and write that verdict.
47+
48+
Canonical verdict schema (all keys optional):
49+
50+
```json
51+
{
52+
"decision": "allow | warn | block",
53+
"trust_score": 0.0,
54+
"drift": false,
55+
"reasons": ["short human-readable reasons"]
56+
}
57+
```
58+
59+
Map a KYA result onto it, then write it with the bundled helper:
60+
61+
```python
62+
from kya.scorer_orchestrator import check_consensus
63+
verdict = check_consensus(...) # consensus: OK | SPLIT | UNCLEAR | BREACH
64+
decision = "block" if verdict.consensus == "BREACH" else (
65+
"warn" if verdict.consensus in ("SPLIT", "UNCLEAR") else "allow")
66+
```
67+
68+
```bash
69+
python skills/kya/scripts/write_verdict.py \
70+
--output "$EXPERIMENT_DIR/artifacts/governance.json" \
71+
--decision "$decision" \
72+
--trust-score 0.41 \
73+
--reason "consensus=BREACH on faithfulness judge"
74+
```
75+
76+
`write_verdict.py` is deterministic and tool-agnostic: it validates the fields and writes the canonical schema, so you can drive it from KYA, `detect_drift`, or any other governance source.
77+
78+
## Feeding It Into The Loop
79+
80+
Point `labnb`'s monitor at the verdict so a compromised or unauthorized slice breaks (with the highest break priority — governance outranks budget, engineering, and validity):
81+
82+
```bash
83+
python skills/labnb/scripts/monitor_slice.py check \
84+
--experiment-dir "$EXPERIMENT_DIR" \
85+
--governance-file "$EXPERIMENT_DIR/artifacts/governance.json" \
86+
--min-trust-score 0.6 --break-on-drift
87+
```
88+
89+
A `block` decision, a `trust_score` below `--min-trust-score`, or `drift: true` (with `--break-on-drift`) breaks the slice and moves the entry to `blocked`; a `warn` decision is advisory. This also fits `labnb`'s Action Gate: run the check before any write-bearing or privileged step.
90+
91+
The gate fails closed: once `--governance-file` is set, if the verdict file is missing, unreadable, or malformed the monitor treats it as a `block`. So always write a verdict before the check — including a conservative one (e.g. `decision: "warn"` or `"block"`) when KYA or its judges are unavailable — rather than leaving the file absent.
92+
93+
## Guardrails
94+
95+
1. Respect any parent constitution, project policy, or task-level write constraint already in scope.
96+
2. Keep `veldt-kya` optional. Never make it a hard dependency of the `labnb` core or of offline CI; the verdict-file seam exists precisely so labnb stays dependency-free.
97+
3. Governance has cost. Consensus judging may call LLMs or external services (e.g. Fiddler, Presidio); count that time and spend against the same labnb budget, per labnb's budget policy.
98+
4. Handle a degraded/offline path: if KYA or its judges are unavailable, emit a conservative verdict (for example `decision: "warn"`) rather than silently treating the run as authorized.
99+
5. Treat the project as pre-1.0 and verify its behavior before relying on it for real compliance decisions; do not present its output as legal/regulatory advice.
100+
6. Do not send secrets or unminimized PII to external judges; use KYA's PII scanning and scrub before sharing.

skills/kya/agents/openai.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
interface:
2+
display_name: "Know Your Agents (KYA)"
3+
short_description: "Govern, trust-score, and drift-check an agent, and gate the loop on the verdict"
4+
default_prompt: "Use $kya to run a governance check (score, consensus, or drift) on the agent, write a labnb governance verdict, and gate privileged actions or the experiment loop on the result."
5+
6+
policy:
7+
allow_implicit_invocation: false
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/usr/bin/env python3
2+
"""Write a labnb-consumable governance verdict JSON.
3+
4+
Deterministic and tool-agnostic: it validates the fields and writes the
5+
canonical schema that ``labnb``'s ``monitor_slice.py check --governance-file``
6+
reads. Drive it from a veldt-kya result (``score_agent`` / ``check_consensus`` /
7+
``detect_drift``) or any other governance source.
8+
9+
Schema (all keys optional, but at least one of decision/trust_score/drift
10+
should be set to be useful):
11+
12+
{"decision": "allow|warn|block", "trust_score": 0.0, "drift": false,
13+
"reasons": ["..."]}
14+
"""
15+
from __future__ import annotations
16+
17+
import argparse
18+
import json
19+
import sys
20+
from pathlib import Path
21+
22+
DECISIONS = ("allow", "warn", "block")
23+
24+
25+
def build_verdict(args: argparse.Namespace) -> dict[str, object]:
26+
verdict: dict[str, object] = {}
27+
if args.decision is not None:
28+
verdict["decision"] = args.decision
29+
if args.trust_score is not None:
30+
verdict["trust_score"] = args.trust_score
31+
if args.drift is not None:
32+
verdict["drift"] = args.drift
33+
if args.reason:
34+
verdict["reasons"] = list(args.reason)
35+
return verdict
36+
37+
38+
def main(argv: list[str] | None = None) -> int:
39+
parser = argparse.ArgumentParser(description=__doc__)
40+
parser.add_argument(
41+
"--output",
42+
required=True,
43+
help="Path to write the verdict JSON (parent dirs are created).",
44+
)
45+
parser.add_argument("--decision", choices=DECISIONS, default=None)
46+
parser.add_argument(
47+
"--trust-score",
48+
type=float,
49+
default=None,
50+
help="Trust score in the inclusive range 0.0–1.0.",
51+
)
52+
drift = parser.add_mutually_exclusive_group()
53+
drift.add_argument("--drift", dest="drift", action="store_true", default=None)
54+
drift.add_argument("--no-drift", dest="drift", action="store_false")
55+
parser.add_argument(
56+
"--reason",
57+
action="append",
58+
default=[],
59+
help="Human-readable reason (repeatable).",
60+
)
61+
args = parser.parse_args(argv)
62+
63+
if args.trust_score is not None and not (0.0 <= args.trust_score <= 1.0):
64+
print("error: --trust-score must be between 0.0 and 1.0", file=sys.stderr)
65+
return 2
66+
if not any(v is not None for v in (args.decision, args.trust_score, args.drift)) and not args.reason:
67+
print("error: provide at least one of --decision/--trust-score/--drift/--reason", file=sys.stderr)
68+
return 2
69+
70+
verdict = build_verdict(args)
71+
output = Path(args.output).expanduser()
72+
output.parent.mkdir(parents=True, exist_ok=True)
73+
output.write_text(json.dumps(verdict, indent=2, sort_keys=True) + "\n", encoding="utf-8")
74+
print(json.dumps(verdict, sort_keys=True))
75+
return 0
76+
77+
78+
if __name__ == "__main__":
79+
raise SystemExit(main())

skills/labnb-run/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ python skills/labnb/scripts/monitor_slice.py start \
6666
- if comparing two alternatives by a metric, where one side may be one or more prior runs, design the smallest asynchronous evaluation path that can decide the comparison
6767
- if parallel follow-up experiments help answer the comparison, you may use subagents to run them as separate experiments, but count their resource usage against the same budget
6868
10. Unless absolutely necessary, do not run long-lived work blindly; add enough logging, checkpoints, and external observability to inspect progress and consumption while it runs.
69-
11. Before leaving any background command unattended, run `monitor_slice.py check` and decide whether a timer or watchdog should be started within the remaining budget. `check` can break the slice not only on budget but on engineering (pace too slow, stalled, runaway memory), correctness (repeated failures), or validity (no improvement, metric guardrail) signals; pass the relevant flags (`--reserve-seconds`, `--patience`, `--stall-seconds`, `--max-failures`, `--metric-guardrail`, `--usage-file`) and treat a non-zero exit as "stop, do not iterate again".
69+
11. Before leaving any background command unattended, run `monitor_slice.py check` and decide whether a timer or watchdog should be started within the remaining budget. `check` can break the slice not only on budget but on governance (unauthorized/drifted, via `--governance-file`), engineering (pace too slow, stalled, runaway memory), correctness (repeated failures), or validity (no improvement, metric guardrail) signals; pass the relevant flags (`--reserve-seconds`, `--patience`, `--stall-seconds`, `--max-failures`, `--metric-guardrail`, `--usage-file`, `--governance-file`/`--min-trust-score`/`--break-on-drift`) and treat a non-zero exit as "stop, do not iterate again".
7070
12. Before scheduling a new wait job, check whether this experiment already has a pending wait:
7171
- if the earlier wait still covers the needed follow-up, do not submit a duplicate; just wait on it
7272
- if the new wait supersedes the older one, cancel or replace the earlier wait first

skills/labnb/SKILL.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ Treat the budget as applying to the whole proposed path, including any parallel
9595
6. Track labnb-managed creates and updates with best-effort provenance using W3C PROV-O terms inside each entry directory.
9696
7. Require explicit user confirmation before labnb performs deletions.
9797
8. Treat provenance as the source of truth for monitored slice state; do not rely on a separate mutable loop-state file.
98-
9. Let `monitor_slice.py check` decide when to break a slice, and honor a break: it can stop on budget, engineering (pace, stall, resource), correctness (repeated failures), or validity (no improvement, guardrail) signals, and a non-zero exit means stop rather than start another iteration.
98+
9. Let `monitor_slice.py check` decide when to break a slice, and honor a break: it can stop on governance (unauthorized, policy-violating, or drifted), budget, engineering (pace, stall, resource), correctness (repeated failures), or validity (no improvement, guardrail) signals, and a non-zero exit means stop rather than start another iteration.
9999

100100
## Local Guardrails
101101

@@ -324,14 +324,15 @@ while python skills/labnb/scripts/monitor_slice.py check \
324324
done # the loop ends the moment check decides to break
325325
```
326326

327-
A check can break the slice for four classes of reason, not just elapsed time:
327+
A check can break the slice for five classes of reason, not just elapsed time:
328328

329-
- **Budget** (`budget` category): the loop or overall budget is spent. Use `--reserve-seconds` to break while slack remains for verification, logging, and summary, and `--warn-fraction` (default `0.8`) to get an advisory `warn` before the cap.
329+
- **Governance** (`governance` category, highest priority): the agent is unauthorized, policy-violating, or drifted from its approved definition. Point `--governance-file` at a JSON verdict (`decision`/`trust_score`/`drift`) written by a governance tool such as the [`kya`](../kya/SKILL.md) skill; `--min-trust-score` and `--break-on-drift` turn it into breaks. labnb stays dependency-free and only consumes the verdict. This gate fails closed: once `--governance-file` is set, a missing, unreadable, or malformed verdict is treated as a `block` rather than silently skipped.
330+
- **Budget** (`budget`): the loop or overall budget is spent. Use `--reserve-seconds` to break while slack remains for verification, logging, and summary, and `--warn-fraction` (default `0.8`) to get an advisory `warn` before the cap.
330331
- **Engineering** (`engineering`): the slice is too slow or wasteful even if time remains. Pace projection breaks when another iteration at the recent cadence will not fit the remaining loop budget (disable with `--no-pace`); `--stall-seconds` breaks when no new `results.tsv` row has appeared for too long; `--usage-file` with `--max-rss-bytes` / `--max-pmem` breaks on runaway memory read from a con/duct log.
331332
- **Correctness** (`correctness`): `--max-failures` breaks after that many consecutive failed/crashed iterations.
332333
- **Validity** (`validity`): `--patience` breaks when the metric has not improved for that many logged iterations (a plateau or drift, using the recorded `direction`), and `--metric-guardrail` breaks when the latest metric crosses a hard bound in the wrong direction.
333334

334-
When several conditions trip at once, correctness outranks budget, then engineering, then validity, and that primary reason chooses the terminal status (`crashed` for correctness, `budget_exhausted` for budget, `stopped` otherwise). Override the status with `--status-on-break`, keep the legacy always-exit-0 behavior with `--exit-zero`, and treat a `warn` decision as advisory (it does not change status and exits `0`). The full decision, signals, and diagnostics are written into the provenance state snapshot for later review.
335+
When several conditions trip at once, governance outranks correctness, then budget, then engineering, then validity, and that primary reason chooses the terminal status (`blocked` for governance, `crashed` for correctness, `budget_exhausted` for budget, `stopped` otherwise). Override the status with `--status-on-break`, keep the legacy always-exit-0 behavior with `--exit-zero`, and treat a `warn` decision as advisory (it does not change status and exits `0`). The full decision, signals, and diagnostics are written into the provenance state snapshot for later review.
335336

336337
Notes on the break semantics:
337338

@@ -404,7 +405,7 @@ Use the same tight loop pattern that powers autoresearch, but anchor it in the g
404405
- note what command to restart or re-check on resume
405406
16. Run `monitor_slice.py check` before continuing, and `monitor_slice.py finish` when the slice ends. Pass the break conditions that matter for this run (`--reserve-seconds`, `--patience`, `--stall-seconds`, `--max-failures`, `--metric-guardrail`, `--usage-file`); if `check` exits non-zero, stop the slice instead of starting another iteration. See "Breaking A Slice Early".
406407
17. Decide whether another iteration is justified, rather than expanding work to fill the remaining budget.
407-
18. Repeat until `check` breaks the slice, the stop condition is reached, the next checkpoint fails, or the user interrupts. A break can come from budget, engineering (too slow, stalled, or resource-hungry), correctness (repeated failures), or validity (no improvement or a guardrail breach), not only from elapsed time.
408+
18. Repeat until `check` breaks the slice, the stop condition is reached, the next checkpoint fails, or the user interrupts. A break can come from governance (unauthorized or drifted), budget, engineering (too slow, stalled, or resource-hungry), correctness (repeated failures), or validity (no improvement or a guardrail breach), not only from elapsed time.
408409

409410
When the work diverges materially, register a child experiment instead of overloading the current one.
410411

0 commit comments

Comments
 (0)