goldenreq 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- goldenreq-0.3.0/PKG-INFO +5 -0
- goldenreq-0.3.0/README.md +51 -0
- goldenreq-0.3.0/pyproject.toml +18 -0
- goldenreq-0.3.0/setup.cfg +4 -0
- goldenreq-0.3.0/src/goldenreq/__init__.py +30 -0
- goldenreq-0.3.0/src/goldenreq/cli.py +60 -0
- goldenreq-0.3.0/src/goldenreq/effects.py +105 -0
- goldenreq-0.3.0/src/goldenreq/queue.py +132 -0
- goldenreq-0.3.0/src/goldenreq/verdict.py +133 -0
- goldenreq-0.3.0/src/goldenreq.egg-info/PKG-INFO +5 -0
- goldenreq-0.3.0/src/goldenreq.egg-info/SOURCES.txt +13 -0
- goldenreq-0.3.0/src/goldenreq.egg-info/dependency_links.txt +1 -0
- goldenreq-0.3.0/src/goldenreq.egg-info/entry_points.txt +2 -0
- goldenreq-0.3.0/src/goldenreq.egg-info/top_level.txt +1 -0
- goldenreq-0.3.0/tests/test_goldenreq.py +166 -0
goldenreq-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# goldenreq β the acceptance gate for goldenization
|
|
2
|
+
|
|
3
|
+
**The gauge is not the handler.** A *gauge* is an internal self-assessment β the
|
|
4
|
+
bandit's 2Γπ on a constructed chain, a rulecatcher candidate rule, a forged
|
|
5
|
+
template, a personapack `activation_grade` claim. A gauge can detect quality; it
|
|
6
|
+
may **never** certify itself into golden/adopted/canon. That certification is a
|
|
7
|
+
*handler*'s job β an external acceptance authority. `goldenreq` is that gate.
|
|
8
|
+
|
|
9
|
+
The two-knob / CC loop is acceptable **only if** goldenization goes through this
|
|
10
|
+
gate. Before, chainaios's bandit rule told the agent to self-fold a 2Γπ chain
|
|
11
|
+
into golden β self-certification. Now the rule says: **file a goldenization
|
|
12
|
+
request; promotion happens only on an accepted verdict.**
|
|
13
|
+
|
|
14
|
+
## The flow
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
gauge ββfile_request(kind, candidate_id, evidence, requested_promotion, requester)βββΊ queue (pending)
|
|
18
|
+
β
|
|
19
|
+
handler ββaccept(request_id, by, reason) βββΊ fires the promotion effect βββΊ golden / grade-bump / β¦
|
|
20
|
+
ββreject(request_id, by, reason) βββΊ NO effect; reason recorded (a hard negative)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- **Requests carry receipts.** `evidence` is the graded KB notes / cert ids /
|
|
24
|
+
forge reports behind the claim. An unevidenced ask is a weak ask.
|
|
25
|
+
- **Verdicts are the seed corpus** for the learned acceptance model that will
|
|
26
|
+
one day staff this gate automatically. **Rejections (with reasons) are its
|
|
27
|
+
hard negatives.** No trace β no handler β not annealable, only believable.
|
|
28
|
+
|
|
29
|
+
## Kinds β effects
|
|
30
|
+
|
|
31
|
+
| kind | gauge upstream | effect on accept |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| `chain-golden` | a bandit chain earned π twice | append an `ACCEPTED-BY` marker into the GBA KB note (never rewrites agent notes) |
|
|
34
|
+
| `pack-grade-bump` | a personapack PackEntry | **pure** helper returns the new `activation_grade`; personapack applies it |
|
|
35
|
+
| `rule-adopt` | a rulecatcher candidate | **stub** β points at `rulecatcher.engine.adopt_rules` (no fake integration) |
|
|
36
|
+
| `template-promote` | a forged language/template | **stub** β no canonical-template promotion API to call yet |
|
|
37
|
+
|
|
38
|
+
## Record shape
|
|
39
|
+
|
|
40
|
+
The verdict record mirrors `base/acceptance`'s `acceptance.schema.Verdict`
|
|
41
|
+
(read via `git show acceptance-handler:β¦`) so the verdicts JSONL is drop-in for
|
|
42
|
+
that reader β **by shape, not by import** (no cross-branch coupling). The
|
|
43
|
+
field-by-field mapping is documented at the top of `verdict.py`.
|
|
44
|
+
|
|
45
|
+
## CLI
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
goldenreq list # the handler's inbox (pending requests)
|
|
49
|
+
goldenreq accept <id> --by human:alice # rule accept β fire the effect
|
|
50
|
+
goldenreq reject <id> --by human:alice --reason "β¦" # rule reject (reason required)
|
|
51
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "goldenreq"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
description = "The acceptance gate for goldenization: gauges FILE requests carrying receipts; a handler ACCEPTS/REJECTS; only an accept fires the promotion effect. Nothing self-promotes."
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
# Deliberately dependency-free (stdlib only). The verdict record MIRRORS
|
|
11
|
+
# base/acceptance's schema by shape, not by import (no cross-branch coupling).
|
|
12
|
+
dependencies = []
|
|
13
|
+
|
|
14
|
+
[project.scripts]
|
|
15
|
+
goldenreq = "goldenreq.cli:main"
|
|
16
|
+
|
|
17
|
+
[tool.setuptools.packages.find]
|
|
18
|
+
where = ["src"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""goldenreq β the ACCEPTANCE GATE for goldenization.
|
|
2
|
+
|
|
3
|
+
THE LAW (gauge β handler). A *gauge* is an internal self-assessment: the
|
|
4
|
+
bandit's 2Γπ on a chain, a rulecatcher candidate, a forged template, a
|
|
5
|
+
personapack grade claim. A gauge may detect quality but it may NEVER certify
|
|
6
|
+
itself. Promotion to golden/adopted/canon happens only through a *handler* β an
|
|
7
|
+
external acceptance authority. This package is that gate.
|
|
8
|
+
|
|
9
|
+
- `queue.file_request(...)` β a gauge FILES a request carrying its receipts
|
|
10
|
+
(evidence). That is the only move a gauge gets. The request sits pending.
|
|
11
|
+
- `verdict.accept / verdict.reject` β a handler rules. Only `accept` fires the
|
|
12
|
+
promotion effect (`effects.dispatch`). `reject` carries a reason.
|
|
13
|
+
|
|
14
|
+
Requests carry receipts; verdicts are the seed corpus for the learned acceptance
|
|
15
|
+
model that will one day staff this gate; rejections (with reasons) are its hard
|
|
16
|
+
negatives. The verdict record mirrors base/acceptance's `Verdict` schema so the
|
|
17
|
+
corpus is drop-in for that reader (field mapping: see `verdict.py`).
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
__version__ = "0.3.0"
|
|
22
|
+
|
|
23
|
+
from .queue import KINDS, file_request, list_pending, get_request
|
|
24
|
+
from .verdict import accept, reject, list_verdicts
|
|
25
|
+
from . import effects
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"KINDS", "file_request", "list_pending", "get_request",
|
|
29
|
+
"accept", "reject", "list_verdicts", "effects", "__version__",
|
|
30
|
+
]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""cli.py β `goldenreq list | accept | reject` (argparse, stdlib only)."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from .queue import list_pending
|
|
9
|
+
from .verdict import accept, reject
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main(argv: list[str] | None = None) -> int:
|
|
13
|
+
p = argparse.ArgumentParser(prog="goldenreq",
|
|
14
|
+
description="the acceptance gate for goldenization β nothing self-promotes")
|
|
15
|
+
p.add_argument("--queue", default=None, help="queue JSONL path ($GOLDENREQ_QUEUE_PATH)")
|
|
16
|
+
sub = p.add_subparsers(dest="cmd", required=True)
|
|
17
|
+
|
|
18
|
+
lp = sub.add_parser("list", help="list pending requests (the handler's inbox)")
|
|
19
|
+
lp.add_argument("--json", action="store_true", help="emit raw JSON lines")
|
|
20
|
+
|
|
21
|
+
ap = sub.add_parser("accept", help="accept a request β fire its promotion effect")
|
|
22
|
+
ap.add_argument("request_id")
|
|
23
|
+
ap.add_argument("--by", required=True, help="ruling actor (human:<who> / model:<id>)")
|
|
24
|
+
ap.add_argument("--reason", default="")
|
|
25
|
+
|
|
26
|
+
rp = sub.add_parser("reject", help="reject a request (reason = hard negative)")
|
|
27
|
+
rp.add_argument("request_id")
|
|
28
|
+
rp.add_argument("--by", required=True, help="ruling actor (human:<who> / model:<id>)")
|
|
29
|
+
rp.add_argument("--reason", required=True)
|
|
30
|
+
|
|
31
|
+
args = p.parse_args(argv)
|
|
32
|
+
|
|
33
|
+
if args.cmd == "list":
|
|
34
|
+
pending = list_pending(args.queue)
|
|
35
|
+
if args.json:
|
|
36
|
+
for r in pending:
|
|
37
|
+
print(json.dumps(r, sort_keys=True))
|
|
38
|
+
elif not pending:
|
|
39
|
+
print("no pending requests")
|
|
40
|
+
else:
|
|
41
|
+
for r in pending:
|
|
42
|
+
print(f"{r['request_id']} {r['kind']:<16} {r['candidate_id']} "
|
|
43
|
+
f"β {r['requested_promotion']} (by {r['requester']}, "
|
|
44
|
+
f"{len(r.get('evidence', []))} receipt(s))")
|
|
45
|
+
return 0
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
if args.cmd == "accept":
|
|
49
|
+
v = accept(args.request_id, args.by, args.reason, queue_path=args.queue)
|
|
50
|
+
else:
|
|
51
|
+
v = reject(args.request_id, args.by, args.reason, queue_path=args.queue)
|
|
52
|
+
except (ValueError, FileNotFoundError, NotImplementedError) as e:
|
|
53
|
+
print(f"error: {e}", file=sys.stderr)
|
|
54
|
+
return 1
|
|
55
|
+
print(json.dumps(v, sort_keys=True, indent=2))
|
|
56
|
+
return 0
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if __name__ == "__main__":
|
|
60
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""effects.py β the promotion effects, dispatched ONLY on an accepted verdict.
|
|
2
|
+
|
|
3
|
+
`dispatch` routes an accepted request to its effect by `kind`. An effect is the
|
|
4
|
+
thing the gauge was NOT allowed to do itself; here it runs because a handler
|
|
5
|
+
accepted. Each returns an `outcome` dict that verdict.py stores in the record.
|
|
6
|
+
|
|
7
|
+
Honesty rule: two effects are real (they have a testable local target), two are
|
|
8
|
+
stubs (they'd need cross-package wiring we cannot exercise here). The stubs
|
|
9
|
+
raise `NotImplementedError` and point at the exact upstream call to make β we do
|
|
10
|
+
NOT fake an integration we can't test.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
# the personapack activation_grade ladder, BEST β WORST.
|
|
17
|
+
# Mirror of personapack.entry.GRADES (kept as a literal so goldenreq stays
|
|
18
|
+
# dependency-free; a drift here is caught by the pack-grade-bump test).
|
|
19
|
+
PACK_GRADES: tuple[str, ...] = ("tested-with-receipt", "proven-in-forge", "seed")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def dispatch(request: dict, *, by: str, **kwargs) -> dict:
|
|
23
|
+
"""Route an ACCEPTED request to its promotion effect. Returns the outcome dict."""
|
|
24
|
+
kind = request.get("kind")
|
|
25
|
+
if kind == "chain-golden":
|
|
26
|
+
return mark_chain_golden(request, by=by, **kwargs)
|
|
27
|
+
if kind == "pack-grade-bump":
|
|
28
|
+
new_grade = bump_pack_grade(request.get("requested_promotion") or _current_from(request))
|
|
29
|
+
return {"effect": "pack-grade-bump", "new_grade": new_grade,
|
|
30
|
+
"note": "pure result β personapack applies it to the PackEntry"}
|
|
31
|
+
if kind == "rule-adopt":
|
|
32
|
+
return adopt_rule(request, by=by, **kwargs)
|
|
33
|
+
if kind == "template-promote":
|
|
34
|
+
return promote_template(request, by=by, **kwargs)
|
|
35
|
+
raise ValueError(f"no effect for kind {request.get('kind')!r}")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _current_from(request: dict) -> str:
|
|
39
|
+
# if requested_promotion wasn't a grade, fall back to the weakest rung
|
|
40
|
+
return "seed"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# ββ chain-golden: append an ACCEPTED-BY marker into the GBA KB note ββββββββββ
|
|
44
|
+
def mark_chain_golden(request: dict, *, by: str, **_) -> dict:
|
|
45
|
+
"""Write the golden marker into the GBA KB.
|
|
46
|
+
|
|
47
|
+
`candidate_id` is the path to the graded KB note (a `kb/<topic>.md`, headed
|
|
48
|
+
with a `glyphs:` grade line β see chainaios bandit.kb_instructions). We
|
|
49
|
+
APPEND an ACCEPTED-BY line; we NEVER rewrite the agent's own notes. The
|
|
50
|
+
marker is what turns "the gauge claims ππ" into "a handler certified it
|
|
51
|
+
golden" β the thing self-folding used to do without authority.
|
|
52
|
+
"""
|
|
53
|
+
note = Path(request["candidate_id"])
|
|
54
|
+
if not note.exists():
|
|
55
|
+
raise FileNotFoundError(f"KB note not found: {note}")
|
|
56
|
+
marker = (f"\nACCEPTED-BY: {by} β goldenized on request {request['request_id']} "
|
|
57
|
+
f"(promotion: {request.get('requested_promotion')}); "
|
|
58
|
+
f"evidence: {', '.join(request.get('evidence', [])) or 'none'}. ππ β golden\n")
|
|
59
|
+
with open(note, "a", encoding="utf-8") as f:
|
|
60
|
+
f.write(marker)
|
|
61
|
+
return {"effect": "chain-golden", "kb_note": str(note), "marker": marker.strip()}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ββ pack-grade-bump: a PURE function personapack applies itself ββββββββββββββ
|
|
65
|
+
def bump_pack_grade(current_grade: str) -> str:
|
|
66
|
+
"""Return the next-higher personapack `activation_grade`, or the same grade
|
|
67
|
+
if already at the top. PURE β personapack constructs the new PackEntry with it.
|
|
68
|
+
|
|
69
|
+
Ladder (bestβworst): tested-with-receipt > proven-in-forge > seed.
|
|
70
|
+
A bump moves ONE rung toward tested-with-receipt.
|
|
71
|
+
"""
|
|
72
|
+
if current_grade not in PACK_GRADES:
|
|
73
|
+
raise ValueError(f"grade {current_grade!r} not in {PACK_GRADES}")
|
|
74
|
+
i = PACK_GRADES.index(current_grade)
|
|
75
|
+
return PACK_GRADES[max(0, i - 1)]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# ββ honest stubs: real cross-package integration we cannot test here βββββββββ
|
|
79
|
+
def adopt_rule(request: dict, *, by: str, **_) -> dict:
|
|
80
|
+
"""STUB. A real rule-adopt would call rulecatcher's adopt path:
|
|
81
|
+
|
|
82
|
+
rulecatcher.engine.adopt_rules(connection, rule_ids, actor=by,
|
|
83
|
+
source="goldenreq", reason=<verdict reason>)
|
|
84
|
+
|
|
85
|
+
(see packages/rulecatcher/src/rulecatcher/engine.py, `adopt_rules`).
|
|
86
|
+
That needs a live rulecatcher sqlite `Connection` and real candidate rule ids
|
|
87
|
+
threaded through the request β wiring goldenreq cannot construct or exercise
|
|
88
|
+
in isolation. Not faked.
|
|
89
|
+
"""
|
|
90
|
+
raise NotImplementedError(
|
|
91
|
+
"rule-adopt: wire request.candidate_id (rule ids) + a rulecatcher "
|
|
92
|
+
"Connection to rulecatcher.engine.adopt_rules(actor=by, source='goldenreq'). "
|
|
93
|
+
"Deliberately unimplemented β no fake integration.")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def promote_template(request: dict, *, by: str, **_) -> dict:
|
|
97
|
+
"""STUB. A real template-promote would move a forged language/template from
|
|
98
|
+
candidate into the canonical set (the chainaios `construct_language` /
|
|
99
|
+
template store). That store's promotion API is not defined yet, so there is
|
|
100
|
+
nothing honest to call. Not faked.
|
|
101
|
+
"""
|
|
102
|
+
raise NotImplementedError(
|
|
103
|
+
"template-promote: no canonical-template promotion API exists to call "
|
|
104
|
+
"yet; when one does, dispatch request.candidate_id to it here. "
|
|
105
|
+
"Deliberately unimplemented β no fake integration.")
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""queue.py β the goldenization request queue (append-only JSONL).
|
|
2
|
+
|
|
3
|
+
Same append-only ledger idiom as the KB reward record and base/acceptance's
|
|
4
|
+
verdict corpus (a `write` is one JSON line; nothing is ever mutated in place).
|
|
5
|
+
|
|
6
|
+
A REQUEST is a *gauge-passed, handler-pending* event: a gauge β the bandit's
|
|
7
|
+
2Γπ on a constructed chain, a rulecatcher candidate rule, a forged
|
|
8
|
+
language/template, a personapack `activation_grade` claim β asks for a
|
|
9
|
+
promotion it may NOT grant itself. The request carries its RECEIPTS (evidence)
|
|
10
|
+
and sits pending until a handler rules on it (see ``verdict.py``). This is
|
|
11
|
+
JobWorld's ``supposedly_done`` made explicit, and base/acceptance's
|
|
12
|
+
``handler.verdict == "escalate"`` (gauge-passed, handler-pending) as a queue.
|
|
13
|
+
|
|
14
|
+
THE LAW: the gauge never certifies itself. Filing a request is the ONLY thing a
|
|
15
|
+
gauge may do toward promotion; the promotion effect fires only on an accepted
|
|
16
|
+
verdict.
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import uuid
|
|
23
|
+
from datetime import datetime, timezone
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
# the four things that can be promoted (each has a real gauge upstream)
|
|
27
|
+
KINDS: tuple[str, ...] = (
|
|
28
|
+
"chain-golden", # a constructed bandit chain earned π twice (chainaios)
|
|
29
|
+
"rule-adopt", # a rulecatcher candidate rule wants adoption
|
|
30
|
+
"template-promote", # a forged language / template wants to become canon
|
|
31
|
+
"pack-grade-bump", # a personapack PackEntry wants a higher activation_grade
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
QUEUE_ENV = "GOLDENREQ_QUEUE_PATH"
|
|
35
|
+
VERDICTS_ENV = "GOLDENREQ_VERDICTS_PATH"
|
|
36
|
+
DEFAULT_QUEUE = "goldenreq_queue.jsonl"
|
|
37
|
+
DEFAULT_VERDICTS = "verdicts.jsonl"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def now_iso() -> str:
|
|
41
|
+
return datetime.now(timezone.utc).isoformat()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def resolve_queue_path(path: str | os.PathLike | None = None) -> Path:
|
|
45
|
+
return Path(path or os.environ.get(QUEUE_ENV) or DEFAULT_QUEUE)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def resolve_verdicts_path(queue_path: str | os.PathLike | None = None) -> Path:
|
|
49
|
+
"""The verdicts corpus sits beside the queue (or at $GOLDENREQ_VERDICTS_PATH).
|
|
50
|
+
Mirrors base/acceptance's `resolve_corpus_path` β env override, else default."""
|
|
51
|
+
env = os.environ.get(VERDICTS_ENV)
|
|
52
|
+
if env:
|
|
53
|
+
return Path(env)
|
|
54
|
+
return resolve_queue_path(queue_path).parent / DEFAULT_VERDICTS
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _append(path: Path, record: dict) -> None:
|
|
58
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
59
|
+
with open(path, "a", encoding="utf-8") as f:
|
|
60
|
+
f.write(json.dumps(record, sort_keys=True) + "\n")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _read_all(path: Path) -> list[dict]:
|
|
64
|
+
out: list[dict] = []
|
|
65
|
+
if not Path(path).exists():
|
|
66
|
+
return out
|
|
67
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
68
|
+
for line in f:
|
|
69
|
+
line = line.strip()
|
|
70
|
+
if not line:
|
|
71
|
+
continue
|
|
72
|
+
try:
|
|
73
|
+
out.append(json.loads(line))
|
|
74
|
+
except Exception:
|
|
75
|
+
continue
|
|
76
|
+
return out
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def new_request_id() -> str:
|
|
80
|
+
return "gr-" + uuid.uuid4().hex[:24]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def file_request(kind: str, candidate_id: str, evidence: list[str],
|
|
84
|
+
requested_promotion: str, requester: str,
|
|
85
|
+
queue_path: str | os.PathLike | None = None) -> str:
|
|
86
|
+
"""Append one goldenization request to the queue. Returns its request_id.
|
|
87
|
+
|
|
88
|
+
`evidence` = the receipts (graded KB note paths, cert ids, forge reports).
|
|
89
|
+
A request with no evidence is still filed (honest β an unevidenced ask is a
|
|
90
|
+
weak ask a handler will likely reject), but evidence is what an acceptance
|
|
91
|
+
model later learns from, so gauges SHOULD attach it.
|
|
92
|
+
"""
|
|
93
|
+
if kind not in KINDS:
|
|
94
|
+
raise ValueError(f"kind {kind!r} not in {KINDS}")
|
|
95
|
+
rid = new_request_id()
|
|
96
|
+
rec = {
|
|
97
|
+
"request_id": rid,
|
|
98
|
+
"ts": now_iso(),
|
|
99
|
+
"kind": kind,
|
|
100
|
+
"candidate_id": candidate_id,
|
|
101
|
+
"evidence": list(evidence or []),
|
|
102
|
+
"requested_promotion": requested_promotion,
|
|
103
|
+
"requester": requester,
|
|
104
|
+
"status": "pending",
|
|
105
|
+
}
|
|
106
|
+
_append(resolve_queue_path(queue_path), rec)
|
|
107
|
+
return rid
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def get_request(request_id: str, queue_path: str | os.PathLike | None = None) -> dict | None:
|
|
111
|
+
for rec in _read_all(resolve_queue_path(queue_path)):
|
|
112
|
+
if rec.get("request_id") == request_id:
|
|
113
|
+
return rec
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def ruled_request_ids(queue_path: str | os.PathLike | None = None) -> set[str]:
|
|
118
|
+
"""request_ids that already carry a handler verdict (accept OR reject)."""
|
|
119
|
+
vpath = resolve_verdicts_path(queue_path)
|
|
120
|
+
return {v.get("context_ref") for v in _read_all(vpath) if v.get("context_ref")}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def list_pending(queue_path: str | os.PathLike | None = None) -> list[dict]:
|
|
124
|
+
"""Requests with no handler verdict yet β the handler's inbox.
|
|
125
|
+
|
|
126
|
+
A request is pending β no verdict record points at it (verdict.context_ref
|
|
127
|
+
== request_id). That is base/acceptance's `escalate` state: gauge-passed,
|
|
128
|
+
handler-pending.
|
|
129
|
+
"""
|
|
130
|
+
ruled = ruled_request_ids(queue_path)
|
|
131
|
+
return [r for r in _read_all(resolve_queue_path(queue_path))
|
|
132
|
+
if r.get("request_id") not in ruled]
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""verdict.py β the HANDLER ruling on a goldenization request.
|
|
2
|
+
|
|
3
|
+
`accept` / `reject` are the ONLY authority in this package. Nothing self-promotes:
|
|
4
|
+
a promotion effect (effects.py) fires only when a handler `accept`s. A `reject`
|
|
5
|
+
carries a reason β those reasons are the HARD NEGATIVES for the learned
|
|
6
|
+
acceptance model that later staffs this gate.
|
|
7
|
+
|
|
8
|
+
Verdict record shape β MIRRORS base/acceptance's `acceptance.schema.Verdict`
|
|
9
|
+
(read via `git show acceptance-handler:base/acceptance/acceptance/schema.py`).
|
|
10
|
+
We do NOT import across branches; we reproduce the shape as a plain dict so the
|
|
11
|
+
verdicts corpus is drop-in compatible with that reader. Field mapping:
|
|
12
|
+
|
|
13
|
+
base/acceptance Verdict field β goldenreq source
|
|
14
|
+
------------------------------ ----------------------------------------
|
|
15
|
+
id β a fresh verdict id ("vd-β¦")
|
|
16
|
+
ts β ruling timestamp (ISO-8601)
|
|
17
|
+
domain β "goldenize" (base lists this exact domain)
|
|
18
|
+
artifact_ref β request.candidate_id (what was judged)
|
|
19
|
+
context_ref β request.request_id (the request it belongs to)
|
|
20
|
+
gauge.source β request.requester (the self-claiming gauge)
|
|
21
|
+
gauge.score β request.requested_promotion (the gauge's claim)
|
|
22
|
+
gauge.reasons β request.evidence (the receipts it filed)
|
|
23
|
+
handler.verdict β "accept" | "reject" (base also has "escalate"
|
|
24
|
+
= still-pending; a pending request is exactly
|
|
25
|
+
that state, so it has NO verdict record yet)
|
|
26
|
+
handler.actor β `by`, coerced to "human:<who>" / "model:<id>"
|
|
27
|
+
handler.reasons β [reason] (rejections MUST carry one)
|
|
28
|
+
handler.edits_made β None (goldenreq handlers rule, they don't edit)
|
|
29
|
+
outcome β the promotion effect's result on accept, else None
|
|
30
|
+
"""
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import os
|
|
34
|
+
import re
|
|
35
|
+
import uuid
|
|
36
|
+
|
|
37
|
+
from . import effects
|
|
38
|
+
from .queue import (
|
|
39
|
+
get_request, now_iso, resolve_queue_path, resolve_verdicts_path,
|
|
40
|
+
ruled_request_ids, _append, _read_all,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
ACTOR_PATTERN = re.compile(r"^(human|model):.+$") # base/acceptance schema.ACTOR_PATTERN
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def new_verdict_id() -> str:
|
|
47
|
+
return "vd-" + uuid.uuid4().hex[:24]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _actor(by: str) -> str:
|
|
51
|
+
"""Coerce `by` to the base schema's `^(human|model):.+$` actor form.
|
|
52
|
+
A bare name is assumed human (the default handler is a person doing QA)."""
|
|
53
|
+
by = (by or "").strip()
|
|
54
|
+
if not by:
|
|
55
|
+
raise ValueError("a verdict requires `by` (the ruling actor)")
|
|
56
|
+
return by if ACTOR_PATTERN.match(by) else f"human:{by}"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _build_verdict(request: dict, decision: str, by: str,
|
|
60
|
+
reasons: list[str], outcome: dict | None) -> dict:
|
|
61
|
+
"""One acceptance event, in base/acceptance Verdict shape (see module docstring)."""
|
|
62
|
+
return {
|
|
63
|
+
"id": new_verdict_id(),
|
|
64
|
+
"ts": now_iso(),
|
|
65
|
+
"domain": "goldenize",
|
|
66
|
+
"artifact_ref": request["candidate_id"],
|
|
67
|
+
"context_ref": request["request_id"],
|
|
68
|
+
"gauge": {
|
|
69
|
+
"source": request.get("requester", ""),
|
|
70
|
+
"score": request.get("requested_promotion"),
|
|
71
|
+
"reasons": list(request.get("evidence", [])),
|
|
72
|
+
},
|
|
73
|
+
"handler": {
|
|
74
|
+
"verdict": decision,
|
|
75
|
+
"actor": _actor(by),
|
|
76
|
+
"reasons": list(reasons),
|
|
77
|
+
"edits_made": None,
|
|
78
|
+
},
|
|
79
|
+
"outcome": outcome,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _guard(request_id: str, queue_path) -> dict:
|
|
84
|
+
request = get_request(request_id, queue_path=queue_path)
|
|
85
|
+
if request is None:
|
|
86
|
+
raise ValueError(f"no such request: {request_id}")
|
|
87
|
+
if request_id in ruled_request_ids(queue_path):
|
|
88
|
+
raise ValueError(f"request {request_id} already has a verdict (no double-ruling)")
|
|
89
|
+
return request
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def accept(request_id: str, by: str, reason: str = "", *,
|
|
93
|
+
queue_path: str | os.PathLike | None = None,
|
|
94
|
+
verdicts_path: str | os.PathLike | None = None,
|
|
95
|
+
effect_kwargs: dict | None = None) -> dict:
|
|
96
|
+
"""ACCEPT a request β fire its promotion effect, then record the verdict.
|
|
97
|
+
|
|
98
|
+
The effect runs BEFORE the record is written so its result is captured in
|
|
99
|
+
`outcome` (the audit trail). If the effect raises, nothing is recorded and
|
|
100
|
+
the request stays pending β no half-promotion. Returns the verdict record.
|
|
101
|
+
"""
|
|
102
|
+
from pathlib import Path
|
|
103
|
+
request = _guard(request_id, queue_path)
|
|
104
|
+
outcome = effects.dispatch(request, by=_actor(by), **(effect_kwargs or {}))
|
|
105
|
+
verdict = _build_verdict(request, "accept", by, [reason] if reason else [], outcome)
|
|
106
|
+
vpath = Path(verdicts_path) if verdicts_path is not None else resolve_verdicts_path(queue_path)
|
|
107
|
+
_append(vpath, verdict)
|
|
108
|
+
return verdict
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def reject(request_id: str, by: str, reason: str, *,
|
|
112
|
+
queue_path: str | os.PathLike | None = None,
|
|
113
|
+
verdicts_path: str | os.PathLike | None = None) -> dict:
|
|
114
|
+
"""REJECT a request β NO effect, record the verdict with its reason.
|
|
115
|
+
|
|
116
|
+
The reason is mandatory: rejections are the hard negatives the learned
|
|
117
|
+
acceptance model trains on. Nothing is promoted.
|
|
118
|
+
"""
|
|
119
|
+
if not (reason or "").strip():
|
|
120
|
+
raise ValueError("reject requires a reason (it is the hard negative)")
|
|
121
|
+
request = _guard(request_id, queue_path)
|
|
122
|
+
verdict = _build_verdict(request, "reject", by, [reason], None)
|
|
123
|
+
from pathlib import Path
|
|
124
|
+
vpath = Path(verdicts_path) if verdicts_path is not None else resolve_verdicts_path(queue_path)
|
|
125
|
+
_append(vpath, verdict)
|
|
126
|
+
return verdict
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def list_verdicts(queue_path: str | os.PathLike | None = None,
|
|
130
|
+
verdicts_path: str | os.PathLike | None = None) -> list[dict]:
|
|
131
|
+
from pathlib import Path
|
|
132
|
+
vpath = Path(verdicts_path) if verdicts_path is not None else resolve_verdicts_path(queue_path)
|
|
133
|
+
return _read_all(vpath)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/goldenreq/__init__.py
|
|
4
|
+
src/goldenreq/cli.py
|
|
5
|
+
src/goldenreq/effects.py
|
|
6
|
+
src/goldenreq/queue.py
|
|
7
|
+
src/goldenreq/verdict.py
|
|
8
|
+
src/goldenreq.egg-info/PKG-INFO
|
|
9
|
+
src/goldenreq.egg-info/SOURCES.txt
|
|
10
|
+
src/goldenreq.egg-info/dependency_links.txt
|
|
11
|
+
src/goldenreq.egg-info/entry_points.txt
|
|
12
|
+
src/goldenreq.egg-info/top_level.txt
|
|
13
|
+
tests/test_goldenreq.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
goldenreq
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""goldenreq β the acceptance gate. Nothing self-promotes; only an accepted
|
|
2
|
+
verdict fires an effect; the verdict record mirrors base/acceptance's schema."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
import goldenreq
|
|
11
|
+
from goldenreq import effects
|
|
12
|
+
from goldenreq.queue import resolve_verdicts_path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _q(tmp_path) -> Path:
|
|
16
|
+
return tmp_path / "goldenreq_queue.jsonl"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ββ file β list β accept β effect (chain-golden marker lands in a GBA KB) ββββ
|
|
20
|
+
def test_file_list_accept_chain_golden_marker_lands(tmp_path):
|
|
21
|
+
q = _q(tmp_path)
|
|
22
|
+
# a real GBA KB note, headed with a glyphs grade line (chainaios kb_instructions shape)
|
|
23
|
+
kb = tmp_path / "kb"; kb.mkdir()
|
|
24
|
+
note = kb / "rollback-chain.md"
|
|
25
|
+
original = "glyphs: ππ\n\n# rollback chain\n\nConstructed chain that earned π twice.\n"
|
|
26
|
+
note.write_text(original)
|
|
27
|
+
|
|
28
|
+
rid = goldenreq.file_request(
|
|
29
|
+
"chain-golden", str(note),
|
|
30
|
+
evidence=[str(note), "round-7 reward log"],
|
|
31
|
+
requested_promotion="golden", requester="triage-bandit", queue_path=q)
|
|
32
|
+
assert rid.startswith("gr-")
|
|
33
|
+
|
|
34
|
+
pending = goldenreq.list_pending(q)
|
|
35
|
+
assert len(pending) == 1 and pending[0]["request_id"] == rid
|
|
36
|
+
assert pending[0]["status"] == "pending"
|
|
37
|
+
|
|
38
|
+
verdict = goldenreq.accept(rid, "human:alice", "chain proved out twice", queue_path=q)
|
|
39
|
+
|
|
40
|
+
# the promotion effect actually ran: marker APPENDED, original NOT rewritten
|
|
41
|
+
body = note.read_text()
|
|
42
|
+
assert body.startswith(original) # agent's note untouched
|
|
43
|
+
assert "ACCEPTED-BY: human:alice" in body
|
|
44
|
+
assert "ππ β golden" in body
|
|
45
|
+
assert verdict["handler"]["verdict"] == "accept"
|
|
46
|
+
assert verdict["outcome"]["effect"] == "chain-golden"
|
|
47
|
+
|
|
48
|
+
# once ruled, it leaves the pending inbox
|
|
49
|
+
assert goldenreq.list_pending(q) == []
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# ββ reject β no effect + reason recorded as a hard negative ββββββββββββββββββ
|
|
53
|
+
def test_reject_records_reason_and_fires_no_effect(tmp_path):
|
|
54
|
+
q = _q(tmp_path)
|
|
55
|
+
kb = tmp_path / "kb"; kb.mkdir()
|
|
56
|
+
note = kb / "flaky-chain.md"
|
|
57
|
+
original = "glyphs: π\n\n# flaky chain\n"
|
|
58
|
+
note.write_text(original)
|
|
59
|
+
|
|
60
|
+
rid = goldenreq.file_request("chain-golden", str(note), evidence=[str(note)],
|
|
61
|
+
requested_promotion="golden", requester="triage-bandit",
|
|
62
|
+
queue_path=q)
|
|
63
|
+
verdict = goldenreq.reject(rid, "human:alice", "only one π, evidence thin", queue_path=q)
|
|
64
|
+
|
|
65
|
+
assert verdict["handler"]["verdict"] == "reject"
|
|
66
|
+
assert verdict["handler"]["reasons"] == ["only one π, evidence thin"]
|
|
67
|
+
assert verdict["outcome"] is None
|
|
68
|
+
assert note.read_text() == original # NO effect ran
|
|
69
|
+
assert goldenreq.list_pending(q) == [] # ruled β out of inbox
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_reject_requires_a_reason(tmp_path):
|
|
73
|
+
q = _q(tmp_path)
|
|
74
|
+
rid = goldenreq.file_request("chain-golden", "/x", evidence=[], requested_promotion="golden",
|
|
75
|
+
requester="b", queue_path=q)
|
|
76
|
+
with pytest.raises(ValueError):
|
|
77
|
+
goldenreq.reject(rid, "human:alice", "", queue_path=q)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_no_double_ruling(tmp_path):
|
|
81
|
+
q = _q(tmp_path)
|
|
82
|
+
note = tmp_path / "n.md"; note.write_text("glyphs: ππ\n")
|
|
83
|
+
rid = goldenreq.file_request("chain-golden", str(note), evidence=[], requested_promotion="golden",
|
|
84
|
+
requester="b", queue_path=q)
|
|
85
|
+
goldenreq.accept(rid, "human:alice", queue_path=q)
|
|
86
|
+
with pytest.raises(ValueError):
|
|
87
|
+
goldenreq.reject(rid, "human:alice", "changed my mind", queue_path=q)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# ββ verdict records match the documented base/acceptance field mapping βββββββ
|
|
91
|
+
def test_verdict_record_mirrors_base_acceptance_schema(tmp_path):
|
|
92
|
+
q = _q(tmp_path)
|
|
93
|
+
note = tmp_path / "n.md"; note.write_text("glyphs: ππ\n")
|
|
94
|
+
rid = goldenreq.file_request(
|
|
95
|
+
"chain-golden", str(note), evidence=["ev-a", "ev-b"],
|
|
96
|
+
requested_promotion="golden", requester="triage-bandit", queue_path=q)
|
|
97
|
+
v = goldenreq.accept(rid, "alice", "ok", queue_path=q) # bare name β coerced to human:
|
|
98
|
+
|
|
99
|
+
# the exact keys base/acceptance.schema.Verdict defines
|
|
100
|
+
assert set(v) == {"id", "ts", "domain", "artifact_ref", "context_ref",
|
|
101
|
+
"gauge", "handler", "outcome"}
|
|
102
|
+
assert v["id"].startswith("vd-")
|
|
103
|
+
assert v["domain"] == "goldenize" # base lists this domain
|
|
104
|
+
assert v["artifact_ref"] == str(note) # β request.candidate_id
|
|
105
|
+
assert v["context_ref"] == rid # β request.request_id
|
|
106
|
+
# gauge = the internal claim (never authority)
|
|
107
|
+
assert set(v["gauge"]) == {"source", "score", "reasons"}
|
|
108
|
+
assert v["gauge"]["source"] == "triage-bandit" # β request.requester
|
|
109
|
+
assert v["gauge"]["score"] == "golden" # β request.requested_promotion
|
|
110
|
+
assert v["gauge"]["reasons"] == ["ev-a", "ev-b"] # β request.evidence
|
|
111
|
+
# handler = the external authority; actor matches base's ^(human|model):.+$
|
|
112
|
+
assert set(v["handler"]) == {"verdict", "actor", "reasons", "edits_made"}
|
|
113
|
+
assert v["handler"]["actor"] == "human:alice"
|
|
114
|
+
assert v["handler"]["verdict"] in {"accept", "reject"}
|
|
115
|
+
assert v["handler"]["edits_made"] is None
|
|
116
|
+
|
|
117
|
+
# it round-trips as a JSONL corpus line (drop-in for the base reader)
|
|
118
|
+
vpath = resolve_verdicts_path(q)
|
|
119
|
+
line = vpath.read_text().splitlines()[-1]
|
|
120
|
+
assert json.loads(line)["id"] == v["id"]
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# ββ pack-grade-bump is a pure ladder step ββββββββββββββββββββββββββββββββββββ
|
|
124
|
+
def test_bump_pack_grade_is_pure_and_matches_personapack_ladder():
|
|
125
|
+
# ladder bestβworst: tested-with-receipt > proven-in-forge > seed
|
|
126
|
+
assert effects.bump_pack_grade("seed") == "proven-in-forge"
|
|
127
|
+
assert effects.bump_pack_grade("proven-in-forge") == "tested-with-receipt"
|
|
128
|
+
assert effects.bump_pack_grade("tested-with-receipt") == "tested-with-receipt" # capped at top
|
|
129
|
+
with pytest.raises(ValueError):
|
|
130
|
+
effects.bump_pack_grade("made-up")
|
|
131
|
+
# drift guard: our literal ladder equals personapack's GRADES
|
|
132
|
+
from personapack.entry import GRADES
|
|
133
|
+
assert effects.PACK_GRADES == GRADES
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_pack_grade_bump_via_accept(tmp_path):
|
|
137
|
+
q = _q(tmp_path)
|
|
138
|
+
rid = goldenreq.file_request("pack-grade-bump", "some-persona", evidence=["gba-receipt"],
|
|
139
|
+
requested_promotion="proven-in-forge", requester="personapack",
|
|
140
|
+
queue_path=q)
|
|
141
|
+
v = goldenreq.accept(rid, "human:alice", queue_path=q)
|
|
142
|
+
assert v["outcome"]["effect"] == "pack-grade-bump"
|
|
143
|
+
assert v["outcome"]["new_grade"] == "tested-with-receipt"
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# ββ the honest stubs raise NotImplementedError (no fake integration) βββββββββ
|
|
147
|
+
def test_rule_adopt_and_template_promote_are_honest_stubs(tmp_path):
|
|
148
|
+
q = _q(tmp_path)
|
|
149
|
+
for kind in ("rule-adopt", "template-promote"):
|
|
150
|
+
rid = goldenreq.file_request(kind, "cand", evidence=[], requested_promotion="adopt",
|
|
151
|
+
requester="g", queue_path=q)
|
|
152
|
+
with pytest.raises(NotImplementedError):
|
|
153
|
+
goldenreq.accept(rid, "human:alice", queue_path=q)
|
|
154
|
+
# the failed effect left the request PENDING (no half-promotion, no verdict)
|
|
155
|
+
assert any(r["request_id"] == rid for r in goldenreq.list_pending(q))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# ββ the chainaios bandit.py rule text no longer self-promotes (grep test) ββββ
|
|
159
|
+
def test_bandit_rule_text_files_a_request_not_self_fold():
|
|
160
|
+
bandit = (Path(__file__).resolve().parents[2] / "chainaios" / "src" / "chainaios" / "bandit.py")
|
|
161
|
+
text = bandit.read_text()
|
|
162
|
+
assert "FILE A GOLDENIZATION REQUEST" in text
|
|
163
|
+
assert "never self-fold to golden" in text
|
|
164
|
+
assert "promotion" in text and "accepted verdict" in text
|
|
165
|
+
# the old self-certifying instruction is gone
|
|
166
|
+
assert "fold it into a golden chain" not in text
|