feat: engagement close-session wires vault, metrics, Kai ledger (WP-0009 T09)
Add close_session helpers and CLI to append vault session logs, engagement- scoped metrics, duty Kai charges, and client reports. Rejects sensitive summaries. Pilot smoke-closed once; prepare points operators at close-session.
This commit is contained in:
parent
ad5965be91
commit
4532196546
11 changed files with 588 additions and 14 deletions
|
|
@ -25,8 +25,10 @@ from .metrics import MetricsStore, OptimizerStore, performance_summary_markdown
|
|||
from .optimization import OptimizationLoop, MIN_SAMPLES_FOR_RECOMMENDATIONS
|
||||
from .engagement_promote import promote_engagement
|
||||
from .engagement import (
|
||||
DUTY_BASE_KAI,
|
||||
EngagementError,
|
||||
build_prepare_bundle,
|
||||
close_session,
|
||||
export_handoff,
|
||||
find_repo_root,
|
||||
list_engagement_dirs,
|
||||
|
|
@ -1797,6 +1799,119 @@ def engagement_export_handoff(engagement_ref: str, repo_root: Optional[str]):
|
|||
click.echo(f"Handoff pack: {handoff}")
|
||||
|
||||
|
||||
@engagement.command("close-session")
|
||||
@click.argument("engagement_ref")
|
||||
@click.option("--success", "outcome_success", is_flag=True, help="Session succeeded")
|
||||
@click.option("--failure", "outcome_failure", is_flag=True, help="Session failed")
|
||||
@click.option(
|
||||
"--duty",
|
||||
type=click.Choice(sorted(DUTY_BASE_KAI.keys())),
|
||||
default="standard_review",
|
||||
show_default=True,
|
||||
help="Duty product for Kai charge",
|
||||
)
|
||||
@click.option(
|
||||
"--summary",
|
||||
required=True,
|
||||
help="One-line non-secret outcome (session log + report)",
|
||||
)
|
||||
@click.option("--time", "execution_time", type=float, help="Wall time seconds")
|
||||
@click.option("--quality", type=float, help="Quality score 0.0–1.0")
|
||||
@click.option(
|
||||
"--access-class",
|
||||
default="host_observe",
|
||||
show_default=True,
|
||||
type=click.Choice(["read_only", "host_observe", "privileged_ops", "none"]),
|
||||
help="Access surcharge for Kai (none = no surcharge)",
|
||||
)
|
||||
@click.option(
|
||||
"--kai-amount",
|
||||
type=int,
|
||||
default=None,
|
||||
help="Override Kai charge (skip catalog formula)",
|
||||
)
|
||||
@click.option(
|
||||
"--no-metrics", is_flag=True, help="Skip .kaizen/metrics under engagement"
|
||||
)
|
||||
@click.option("--no-ledger", is_flag=True, help="Skip commercial/ledger.jsonl charge")
|
||||
@click.option("--no-report", is_flag=True, help="Skip reports/ duty report")
|
||||
@click.option("--idempotency-key", help="Skip metrics append if key already recorded")
|
||||
@click.option("--repo-root", default=None, help="Repo root (default: auto-detect)")
|
||||
@click.option("--json", "as_json", is_flag=True, help="Machine-readable JSON result")
|
||||
def engagement_close_session(
|
||||
engagement_ref: str,
|
||||
outcome_success: bool,
|
||||
outcome_failure: bool,
|
||||
duty: str,
|
||||
summary: str,
|
||||
execution_time: Optional[float],
|
||||
quality: Optional[float],
|
||||
access_class: str,
|
||||
kai_amount: Optional[int],
|
||||
no_metrics: bool,
|
||||
no_ledger: bool,
|
||||
no_report: bool,
|
||||
idempotency_key: Optional[str],
|
||||
repo_root: Optional[str],
|
||||
as_json: bool,
|
||||
):
|
||||
"""Close a duty session: vault log, metrics, Kai ledger, optional report.
|
||||
|
||||
Does not invoke LLMs. Rejects summaries that look like secrets. Metrics land
|
||||
under the engagement tree; ledger lines are billing metadata only.
|
||||
"""
|
||||
eng, _root = _resolve_eng(engagement_ref, repo_root)
|
||||
if outcome_success and outcome_failure:
|
||||
click.echo("Error: use only one of --success or --failure", err=True)
|
||||
sys.exit(1)
|
||||
if not outcome_success and not outcome_failure:
|
||||
click.echo("Error: specify --success or --failure", err=True)
|
||||
sys.exit(1)
|
||||
if quality is not None and not (0.0 <= quality <= 1.0):
|
||||
click.echo("Error: --quality must be between 0.0 and 1.0", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
access = None if access_class == "none" else access_class
|
||||
try:
|
||||
result = close_session(
|
||||
eng,
|
||||
success=outcome_success,
|
||||
duty=duty,
|
||||
summary=summary,
|
||||
execution_time_s=execution_time,
|
||||
quality=quality,
|
||||
access_class=access,
|
||||
amount_kai=kai_amount,
|
||||
record_metrics=not no_metrics,
|
||||
record_ledger=not no_ledger,
|
||||
write_report=not no_report,
|
||||
idempotency_key=idempotency_key,
|
||||
)
|
||||
except EngagementError as exc:
|
||||
click.echo(f"Error: {exc}", err=True)
|
||||
sys.exit(1)
|
||||
except ValueError as exc:
|
||||
click.echo(f"Error: {exc}", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
if as_json:
|
||||
click.echo(json.dumps(result, indent=2))
|
||||
return
|
||||
|
||||
click.echo(f"Closed session: {result['engagement_id']} ({result['duty']})")
|
||||
click.echo(f" Memory: {result.get('memory_path')}")
|
||||
if result.get("report_path"):
|
||||
click.echo(f" Report: {result['report_path']}")
|
||||
if result.get("metrics_recorded"):
|
||||
click.echo(f" Metrics: recorded → {result.get('metrics_path')}")
|
||||
elif not no_metrics:
|
||||
click.echo(" Metrics: skipped (duplicate idempotency key)")
|
||||
if not no_ledger:
|
||||
click.echo(
|
||||
f" Ledger: {result.get('amount_kai', 0)} Kai → {result.get('ledger_path')}"
|
||||
)
|
||||
|
||||
|
||||
@cli.group()
|
||||
def schedule():
|
||||
"""Prepare and validate scheduled agent runs (.kaizen/schedule.yml, ADR-005).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue