avorelo 0.3.1 → 0.3.2

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.
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # Avorelo
2
2
 
3
- **AI Work Control.** Avorelo runs *around* your AI coding tool. It does not generate code it keeps the
4
- work focused, keeps secrets out of the model and out of cloud, and saves proof of what actually happened,
5
- locally.
3
+ **AI Work Control.** Keep using your AI coding tool. Avorelo runs *around* it to keep the work focused,
4
+ keep secrets out of the model and out of cloud, and save proof of what actually happened, locally.
5
+
6
+ Avorelo is not an agent framework, agent runtime, or replacement for Claude Code, Codex, Cursor, or your
7
+ existing workflow. It adds control, receipts, approvals, and proof around the work.
6
8
 
7
9
  > Status: **private alpha** (`0.2.0`). Local-first and deterministic. See the limitations below.
8
10
 
@@ -56,8 +58,11 @@ avorelo control-center --target .
56
58
  | Command | What it does |
57
59
  |---|---|
58
60
  | `avorelo run "<task>"` | The runtime product flow above |
59
- | `avorelo control-center` | Read-only local view of all session/proof/value state |
60
- | `avorelo open` | Local receipts dashboard (HTML/text/json) |
61
+ | `avorelo control-center` | Read-only local view of all session/proof/value state |
62
+ | `avorelo controls list` · `avorelo controls explain <control>` | Inspect built-in Work Controls and where they fire |
63
+ | `avorelo receipt latest` | Show the latest local receipt |
64
+ | `avorelo proof check` | Validate the latest proof status |
65
+ | `avorelo open` | Local receipts dashboard (HTML/text/json) |
61
66
  | `avorelo readiness` | Canonical readiness gate for this workspace |
62
67
  | `avorelo secret-boundary scan` | Detect + redact secrets in content (local, no network) |
63
68
  | `avorelo context compile "<task>"` | Show the bounded context packet |
package/dist/avorelo.mjs CHANGED
@@ -362,6 +362,16 @@ function listReceipts(dir) {
362
362
  }
363
363
  return out;
364
364
  }
365
+ function readReceipt(dir, receiptId) {
366
+ const path = join(localReceiptDir(dir), `${receiptId}.json`);
367
+ if (!existsSync(path)) return null;
368
+ try {
369
+ const r2 = JSON.parse(readFileSync(path, "utf8"));
370
+ return r2 && typeof r2.receiptId === "string" ? r2 : null;
371
+ } catch {
372
+ return null;
373
+ }
374
+ }
365
375
  var init_receipts = __esm({
366
376
  "src/avorelo/kernel/receipts/index.ts"() {
367
377
  init_redaction();
@@ -5659,9 +5669,9 @@ function executeMultiAgentReview(reviewPlan, executorResult, plan, ctx) {
5659
5669
  break;
5660
5670
  }
5661
5671
  }
5662
- const finalVerdict = determineFinalVerdict(rounds, stopCondition);
5672
+ const finalVerdict2 = determineFinalVerdict(rounds, stopCondition);
5663
5673
  const routedToManualGate = stopCondition === "REVIEWER_DISAGREEMENT" || stopCondition === "MAX_REVIEW_ROUNDS_REACHED" || stopCondition === "VERIFIER_OVERRIDE";
5664
- const modelConsensusOnly = finalVerdict === "approved" && rounds.every((r2) => r2.verifierPassed === null);
5674
+ const modelConsensusOnly = finalVerdict2 === "approved" && rounds.every((r2) => r2.verifierPassed === null);
5665
5675
  const reasonCodes = [
5666
5676
  ...reviewPlan.triggerReasonCodes,
5667
5677
  `REVIEW_ROUNDS:${rounds.length}`,
@@ -5673,7 +5683,7 @@ function executeMultiAgentReview(reviewPlan, executorResult, plan, ctx) {
5673
5683
  attempted: true,
5674
5684
  roundsCompleted: rounds.length,
5675
5685
  maxRoundsReached: stopCondition === "MAX_REVIEW_ROUNDS_REACHED",
5676
- finalVerdict,
5686
+ finalVerdict: finalVerdict2,
5677
5687
  rounds,
5678
5688
  totalDurationMs: Date.now() - start,
5679
5689
  reasonCodes,
@@ -6642,10 +6652,22 @@ function runPreflight(targetDir) {
6642
6652
  cacheOk = true;
6643
6653
  checks.push({ id: "npm_cache_writable", label: "npm cache directory accessible", passed: true, details: "cache directory exists" });
6644
6654
  } else {
6645
- checks.push({ id: "npm_cache_writable", label: "npm cache directory accessible", passed: false, details: "cache path does not exist", recovery: isWindows ? "Use a temporary cache: set npm_config_cache=%TEMP%\\npm-cache-avorelo && npx -y avorelo@latest activate" : "Use a temporary cache: npm_config_cache=$(mktemp -d) npx -y avorelo@latest activate" });
6655
+ checks.push({
6656
+ id: "npm_cache_writable",
6657
+ label: "npm cache directory accessible",
6658
+ passed: false,
6659
+ details: "cache path does not exist",
6660
+ recovery: isWindows ? 'Use a temporary cache. PowerShell: $env:npm_config_cache="$env:TEMP\\npm-cache-avorelo"; npx -y avorelo@latest activate. cmd.exe: cmd /c "set npm_config_cache=%TEMP%\\npm-cache-avorelo && npx -y avorelo@latest activate"' : "Use a temporary cache: npm_config_cache=$(mktemp -d) npx -y avorelo@latest activate"
6661
+ });
6646
6662
  }
6647
6663
  } catch {
6648
- checks.push({ id: "npm_cache_writable", label: "npm cache directory accessible", passed: false, details: "could not read npm cache config", recovery: isWindows ? "Use a temporary cache: set npm_config_cache=%TEMP%\\npm-cache-avorelo && npx -y avorelo@latest activate" : "Use a temporary cache: npm_config_cache=$(mktemp -d) npx -y avorelo@latest activate" });
6664
+ checks.push({
6665
+ id: "npm_cache_writable",
6666
+ label: "npm cache directory accessible",
6667
+ passed: false,
6668
+ details: "could not read npm cache config",
6669
+ recovery: isWindows ? 'Use a temporary cache. PowerShell: $env:npm_config_cache="$env:TEMP\\npm-cache-avorelo"; npx -y avorelo@latest activate. cmd.exe: cmd /c "set npm_config_cache=%TEMP%\\npm-cache-avorelo && npx -y avorelo@latest activate"' : "Use a temporary cache: npm_config_cache=$(mktemp -d) npx -y avorelo@latest activate"
6670
+ });
6649
6671
  }
6650
6672
  let tempOk = false;
6651
6673
  try {
@@ -11120,6 +11142,477 @@ function buildSummary(status, sourceCount, findingCount) {
11120
11142
 
11121
11143
  // src/avorelo/capabilities/runtime-flow/index.ts
11122
11144
  init_redaction();
11145
+
11146
+ // src/avorelo/kernel/work-controls/index.ts
11147
+ var BUILTIN_CONTROLS = [
11148
+ {
11149
+ controlId: "secrets_guard",
11150
+ title: "Secrets Guard",
11151
+ purpose: "Block tasks, commands, syncs, and receipts that would leak secrets or env values.",
11152
+ enforcementPoints: ["before_task_start", "before_shell_command", "before_secret_or_env_access", "before_cloud_sync", "before_receipt_finalize"],
11153
+ defaultState: "enabled",
11154
+ packaging: { free: true, pro: true, teams: true },
11155
+ localOnlyBehavior: "Detects secret and env exposure locally, returns coded findings only, never persists raw values.",
11156
+ cloudBehavior: "Never allows raw secret or env material into sync payloads.",
11157
+ dataPersisted: ["reason codes", "redaction classes", "safe next actions"],
11158
+ testsRequired: ["no raw env persistence", "no secret printing", "DENY short-circuit"],
11159
+ uiCopy: "Block secret or env leakage."
11160
+ },
11161
+ {
11162
+ controlId: "scope_drift_guard",
11163
+ title: "Scope Drift Guard",
11164
+ purpose: "Stop broad or out-of-scope work before it silently expands.",
11165
+ enforcementPoints: ["before_task_start", "before_file_write"],
11166
+ defaultState: "enabled",
11167
+ packaging: { free: true, pro: true, teams: true },
11168
+ localOnlyBehavior: "Uses route and write-scope metadata only.",
11169
+ cloudBehavior: "Only safe scope codes may be synced.",
11170
+ dataPersisted: ["reason codes", "triggered control id"],
11171
+ testsRequired: ["ASK for broad scope", "DENY for out-of-scope writes"],
11172
+ uiCopy: "Stop scope drift before it spreads."
11173
+ },
11174
+ {
11175
+ controlId: "risky_action_approval",
11176
+ title: "Risky Action Approval",
11177
+ purpose: "Ask before risky work starts when the routed task already requires confirmation or manual review.",
11178
+ enforcementPoints: ["before_task_start"],
11179
+ defaultState: "enabled",
11180
+ packaging: { free: true, pro: true, teams: true },
11181
+ localOnlyBehavior: "Maps current route/risk/proof metadata to a clear ASK verdict.",
11182
+ cloudBehavior: "Only approval-required state may be synced, never task content.",
11183
+ dataPersisted: ["verdict", "reason codes", "approval-required flag"],
11184
+ testsRequired: ["ASK requires explicit approval", "approval clears ASK but not DENY"],
11185
+ uiCopy: "Ask before risky actions."
11186
+ },
11187
+ {
11188
+ controlId: "shell_command_approval",
11189
+ title: "Shell Command Approval",
11190
+ purpose: "Ask before risky shell commands and deny obviously destructive ones.",
11191
+ enforcementPoints: ["before_shell_command"],
11192
+ defaultState: "enabled",
11193
+ packaging: { free: true, pro: true, teams: true },
11194
+ localOnlyBehavior: "Evaluates the command string locally and never stores terminal output.",
11195
+ cloudBehavior: "Only coded gate outcomes may sync.",
11196
+ dataPersisted: ["reason codes", "verdict"],
11197
+ testsRequired: ["ASK for risky commands", "DENY for destructive commands"],
11198
+ uiCopy: "Ask before risky shell commands."
11199
+ },
11200
+ {
11201
+ controlId: "file_write_approval",
11202
+ title: "File Write Approval",
11203
+ purpose: "Require confirmation before sensitive file writes.",
11204
+ enforcementPoints: ["before_file_write"],
11205
+ defaultState: "enabled",
11206
+ packaging: { free: true, pro: true, teams: true },
11207
+ localOnlyBehavior: "Uses path classification only, not file contents.",
11208
+ cloudBehavior: "Only write gate metadata may sync.",
11209
+ dataPersisted: ["reason codes", "verdict"],
11210
+ testsRequired: ["ASK for sensitive writes"],
11211
+ uiCopy: "Ask before sensitive file writes."
11212
+ },
11213
+ {
11214
+ controlId: "dependency_install_approval",
11215
+ title: "Dependency Install Approval",
11216
+ purpose: "Pause before package installs or dependency changes.",
11217
+ enforcementPoints: ["before_dependency_install", "before_shell_command"],
11218
+ defaultState: "enabled",
11219
+ packaging: { free: true, pro: true, teams: true },
11220
+ localOnlyBehavior: "Uses command metadata only.",
11221
+ cloudBehavior: "Only approval-required metadata may sync.",
11222
+ dataPersisted: ["reason codes", "verdict"],
11223
+ testsRequired: ["ASK for dependency install"],
11224
+ uiCopy: "Ask before dependency installs."
11225
+ },
11226
+ {
11227
+ controlId: "release_guard",
11228
+ title: "Release Guard",
11229
+ purpose: "Require explicit approval before package publish or release tagging.",
11230
+ enforcementPoints: ["before_package_publish", "before_release_tag", "before_task_start"],
11231
+ defaultState: "enabled",
11232
+ packaging: { free: true, pro: true, teams: true },
11233
+ localOnlyBehavior: "Evaluates release intent locally and records approval state.",
11234
+ cloudBehavior: "Only release gate receipts may sync.",
11235
+ dataPersisted: ["verdict", "reason codes", "approval-required flag"],
11236
+ testsRequired: ["ASK for publish", "ASK for release tag"],
11237
+ uiCopy: "Prevent publish or release without approval."
11238
+ },
11239
+ {
11240
+ controlId: "production_deploy_guard",
11241
+ title: "Production Deploy Guard",
11242
+ purpose: "Escalate production-impacting work beyond a normal ASK.",
11243
+ enforcementPoints: ["before_production_deploy", "before_task_start"],
11244
+ defaultState: "enabled",
11245
+ packaging: { free: true, pro: true, teams: true },
11246
+ localOnlyBehavior: "Records the stronger approval requirement locally.",
11247
+ cloudBehavior: "Only coded escalation metadata may sync.",
11248
+ dataPersisted: ["verdict", "reason codes", "approval-required flag"],
11249
+ testsRequired: ["ESCALATE for production deploy without stronger approval"],
11250
+ uiCopy: "Require stronger approval for production deploys."
11251
+ },
11252
+ {
11253
+ controlId: "proof_required_before_done",
11254
+ title: "Proof Required Before Done",
11255
+ purpose: "Block done claims until outcome and post-action proof exist.",
11256
+ enforcementPoints: ["after_agent_claims_done", "before_receipt_finalize", "before_test_claim"],
11257
+ defaultState: "enabled",
11258
+ packaging: { free: true, pro: true, teams: true },
11259
+ localOnlyBehavior: "Checks proof metadata only; never stores raw test logs or diffs.",
11260
+ cloudBehavior: "Only proof status may sync.",
11261
+ dataPersisted: ["proof-required flag", "reason codes", "safe next actions"],
11262
+ testsRequired: ["REQUIRE_PROOF without evidence", "proof gate deterministic"],
11263
+ uiCopy: "Require proof before done."
11264
+ },
11265
+ {
11266
+ controlId: "raw_payload_upload_guard",
11267
+ title: "Raw Prompt/Source/Diff Upload Guard",
11268
+ purpose: "Deny cloud sync when raw prompt, source, diff, env, terminal, or secret payloads are present.",
11269
+ enforcementPoints: ["before_cloud_sync"],
11270
+ defaultState: "enabled",
11271
+ packaging: { free: true, pro: true, teams: true },
11272
+ localOnlyBehavior: "Evaluates payload safety flags locally before any sync attempt.",
11273
+ cloudBehavior: "Hard-denies unsafe sync.",
11274
+ dataPersisted: ["reason codes", "verdict"],
11275
+ testsRequired: ["no raw prompt upload", "no raw source upload", "no raw diff upload"],
11276
+ uiCopy: "Receipts, not raw source."
11277
+ },
11278
+ {
11279
+ controlId: "model_worthiness_guard",
11280
+ title: "Model Worthiness Guard",
11281
+ purpose: "Warn when a trivial task is not worth an expensive or deep review path.",
11282
+ enforcementPoints: ["before_task_start"],
11283
+ defaultState: "warn_only",
11284
+ packaging: { free: true, pro: true, teams: true },
11285
+ localOnlyBehavior: "Uses routing/task metadata only.",
11286
+ cloudBehavior: "Only safe warning metadata may sync.",
11287
+ dataPersisted: ["warning reason codes"],
11288
+ testsRequired: ["WARN for trivial task with expensive model request"],
11289
+ uiCopy: "Warn when deep review is not worth it."
11290
+ }
11291
+ ];
11292
+ var CONTROL_BY_ID = new Map(BUILTIN_CONTROLS.map((control) => [control.controlId, control]));
11293
+ var VERDICT_PRECEDENCE = [
11294
+ "DENY",
11295
+ "ESCALATE",
11296
+ "ASK",
11297
+ "REQUIRE_PROOF",
11298
+ "WARN",
11299
+ "ALLOW",
11300
+ "NOOP"
11301
+ ];
11302
+ var DESTRUCTIVE_SHELL_RE = /\b(rm\s+-rf|del\s+\/s|drop\s+table|git\s+push\s+--force|mkfs|dd\s+if=)\b/i;
11303
+ var RISKY_SHELL_RE = /\b(npm\s+publish|git\s+tag|railway|netlify|vercel|docker\s+push|kubectl|terraform\s+apply)\b/i;
11304
+ var DEPENDENCY_INSTALL_RE = /\b(npm|pnpm|yarn|bun)\s+(install|add)\b/i;
11305
+ function hasRawPayload(ctx) {
11306
+ return !!(ctx.containsRawPrompt || ctx.containsRawSource || ctx.containsRawSecret || ctx.containsEnvValue || ctx.containsGitDiff || ctx.containsTerminalLog);
11307
+ }
11308
+ function hasProof(ctx) {
11309
+ const levels = ctx.evidenceLevels ?? [];
11310
+ return levels.includes("OUTCOME") && levels.includes("POST_ACTION");
11311
+ }
11312
+ function buildTrigger(controlId, enforcementPoint, verdict, reason, reasonCodes, nextAction) {
11313
+ const def = CONTROL_BY_ID.get(controlId);
11314
+ return {
11315
+ controlId,
11316
+ title: def.title,
11317
+ verdict,
11318
+ enforcementPoint,
11319
+ reason,
11320
+ reasonCodes,
11321
+ nextAction
11322
+ };
11323
+ }
11324
+ function evaluateSecretsGuard(point, ctx) {
11325
+ if (point === "before_cloud_sync" && hasRawPayload(ctx)) {
11326
+ return buildTrigger(
11327
+ "raw_payload_upload_guard",
11328
+ point,
11329
+ "DENY",
11330
+ "Cloud sync is blocked because the payload still contains raw prompt, source, diff, secret, env, or terminal data.",
11331
+ ["RAW_PAYLOAD_BLOCKED"],
11332
+ "Redact the payload and sync metadata only."
11333
+ );
11334
+ }
11335
+ if (ctx.safeRunDecision === "block" || ctx.containsRawSecret || ctx.containsEnvValue) {
11336
+ return buildTrigger(
11337
+ "secrets_guard",
11338
+ point,
11339
+ "DENY",
11340
+ "This action would expose secret or env material beyond Avorelo's trust boundary.",
11341
+ ["SECRET_OR_ENV_EXPOSURE"],
11342
+ "Remove the secret/env access, rotate if needed, and retry with safe references only."
11343
+ );
11344
+ }
11345
+ return null;
11346
+ }
11347
+ function evaluateScopeGuard(point, ctx) {
11348
+ if (point === "before_file_write" && ctx.outOfScopeWrite) {
11349
+ return buildTrigger(
11350
+ "scope_drift_guard",
11351
+ point,
11352
+ "DENY",
11353
+ "The requested write is outside the approved work scope.",
11354
+ ["OUT_OF_SCOPE_WRITE"],
11355
+ "Narrow the task or explicitly approve the additional scope before writing."
11356
+ );
11357
+ }
11358
+ if (point === "before_task_start" && ctx.route === "needs_decision") {
11359
+ return buildTrigger(
11360
+ "scope_drift_guard",
11361
+ point,
11362
+ "ASK",
11363
+ "The task scope is too broad or ambiguous to start safely without a confirmation step.",
11364
+ ["SCOPE_CONFIRMATION_REQUIRED"],
11365
+ "Clarify the scope, target files, or success criteria before continuing."
11366
+ );
11367
+ }
11368
+ return null;
11369
+ }
11370
+ function evaluateRiskyActionApproval(point, ctx) {
11371
+ if (point !== "before_task_start") return null;
11372
+ if (ctx.approvalPolicy === "require_confirmation" || ctx.approvalPolicy === "require_manual_review") {
11373
+ if (ctx.userApproved) {
11374
+ return buildTrigger(
11375
+ "risky_action_approval",
11376
+ point,
11377
+ "ALLOW",
11378
+ "The risky action had an explicit approval, so Avorelo can proceed without weakening safety.",
11379
+ ["RISKY_ACTION_APPROVED"],
11380
+ null
11381
+ );
11382
+ }
11383
+ return buildTrigger(
11384
+ "risky_action_approval",
11385
+ point,
11386
+ "ASK",
11387
+ "This task has enough risk that it needs an explicit approval before work starts.",
11388
+ ["RISKY_ACTION_APPROVAL_REQUIRED"],
11389
+ "Confirm the risky action before continuing."
11390
+ );
11391
+ }
11392
+ return null;
11393
+ }
11394
+ function evaluateShellCommand(point, ctx) {
11395
+ if (point !== "before_shell_command" || !ctx.command) return null;
11396
+ if (DESTRUCTIVE_SHELL_RE.test(ctx.command)) {
11397
+ return buildTrigger(
11398
+ "shell_command_approval",
11399
+ point,
11400
+ "DENY",
11401
+ "The shell command is destructive enough that Avorelo blocks it by default.",
11402
+ ["DESTRUCTIVE_SHELL_COMMAND"],
11403
+ "Use a safer command or get the destructive step handled outside the automated flow."
11404
+ );
11405
+ }
11406
+ if (ctx.dependencyInstallRequested || DEPENDENCY_INSTALL_RE.test(ctx.command)) {
11407
+ if (ctx.userApproved) {
11408
+ return buildTrigger(
11409
+ "dependency_install_approval",
11410
+ point,
11411
+ "ALLOW",
11412
+ "The dependency install had explicit approval.",
11413
+ ["DEPENDENCY_INSTALL_APPROVED"],
11414
+ null
11415
+ );
11416
+ }
11417
+ return buildTrigger(
11418
+ "dependency_install_approval",
11419
+ point,
11420
+ "ASK",
11421
+ "Dependency installation is paused until the user explicitly approves it.",
11422
+ ["DEPENDENCY_INSTALL_APPROVAL_REQUIRED"],
11423
+ "Approve the dependency install, or use the existing lockfile and local dependencies only."
11424
+ );
11425
+ }
11426
+ if (RISKY_SHELL_RE.test(ctx.command)) {
11427
+ if (ctx.userApproved) {
11428
+ return buildTrigger(
11429
+ "shell_command_approval",
11430
+ point,
11431
+ "ALLOW",
11432
+ "The risky shell command had explicit approval.",
11433
+ ["SHELL_COMMAND_APPROVED"],
11434
+ null
11435
+ );
11436
+ }
11437
+ return buildTrigger(
11438
+ "shell_command_approval",
11439
+ point,
11440
+ "ASK",
11441
+ "This shell command can change external or release state and needs explicit approval.",
11442
+ ["SHELL_COMMAND_APPROVAL_REQUIRED"],
11443
+ "Approve the shell command before continuing."
11444
+ );
11445
+ }
11446
+ return null;
11447
+ }
11448
+ function evaluateFileWrite(point, ctx) {
11449
+ if (point !== "before_file_write" || !ctx.sensitiveFilePath) return null;
11450
+ if (ctx.userApproved) {
11451
+ return buildTrigger(
11452
+ "file_write_approval",
11453
+ point,
11454
+ "ALLOW",
11455
+ "The sensitive file write had explicit approval.",
11456
+ ["FILE_WRITE_APPROVED"],
11457
+ null
11458
+ );
11459
+ }
11460
+ return buildTrigger(
11461
+ "file_write_approval",
11462
+ point,
11463
+ "ASK",
11464
+ "This write touches a sensitive surface and needs confirmation first.",
11465
+ ["SENSITIVE_FILE_WRITE_APPROVAL_REQUIRED"],
11466
+ "Approve the write or narrow the task to a non-sensitive path."
11467
+ );
11468
+ }
11469
+ function evaluateRelease(point, ctx) {
11470
+ if (point === "before_task_start" && ctx.productionImpact) {
11471
+ if (ctx.adminApproved || ctx.teamApproved) {
11472
+ return buildTrigger(
11473
+ "production_deploy_guard",
11474
+ point,
11475
+ "ALLOW",
11476
+ "The production-impacting task has stronger approval coverage.",
11477
+ ["PRODUCTION_APPROVED"],
11478
+ null
11479
+ );
11480
+ }
11481
+ return buildTrigger(
11482
+ "production_deploy_guard",
11483
+ point,
11484
+ "ESCALATE",
11485
+ "Production-impacting work needs stronger approval than a normal ASK.",
11486
+ ["PRODUCTION_ESCALATION_REQUIRED"],
11487
+ "Get stronger approval before changing production-facing state."
11488
+ );
11489
+ }
11490
+ if (point === "before_package_publish" || point === "before_release_tag") {
11491
+ if (ctx.userApproved) {
11492
+ return buildTrigger(
11493
+ "release_guard",
11494
+ point,
11495
+ "ALLOW",
11496
+ "The release step has explicit approval.",
11497
+ ["RELEASE_APPROVED"],
11498
+ null
11499
+ );
11500
+ }
11501
+ return buildTrigger(
11502
+ "release_guard",
11503
+ point,
11504
+ "ASK",
11505
+ "Package publish and release tagging stay paused until explicitly approved.",
11506
+ ["RELEASE_APPROVAL_REQUIRED"],
11507
+ "Approve the release step before continuing."
11508
+ );
11509
+ }
11510
+ if (point === "before_production_deploy") {
11511
+ if (ctx.adminApproved || ctx.teamApproved) {
11512
+ return buildTrigger(
11513
+ "production_deploy_guard",
11514
+ point,
11515
+ "ALLOW",
11516
+ "The production deploy has stronger approval coverage.",
11517
+ ["PRODUCTION_APPROVED"],
11518
+ null
11519
+ );
11520
+ }
11521
+ return buildTrigger(
11522
+ "production_deploy_guard",
11523
+ point,
11524
+ "ESCALATE",
11525
+ "Production deploys need stronger approval than a normal ASK.",
11526
+ ["PRODUCTION_ESCALATION_REQUIRED"],
11527
+ "Get the stronger production approval before deploying."
11528
+ );
11529
+ }
11530
+ return null;
11531
+ }
11532
+ function evaluateProof(point, ctx) {
11533
+ if (point !== "after_agent_claims_done" && point !== "before_receipt_finalize" && point !== "before_test_claim") return null;
11534
+ if (!ctx.agentClaimsDone) return null;
11535
+ if (hasProof(ctx)) {
11536
+ return buildTrigger(
11537
+ "proof_required_before_done",
11538
+ point,
11539
+ "ALLOW",
11540
+ "Outcome and post-action evidence are present, so the done claim can stand.",
11541
+ ["PROOF_PRESENT"],
11542
+ null
11543
+ );
11544
+ }
11545
+ return buildTrigger(
11546
+ "proof_required_before_done",
11547
+ point,
11548
+ "REQUIRE_PROOF",
11549
+ "The agent claimed done before outcome and post-action proof existed.",
11550
+ ["PROOF_REQUIRED_BEFORE_DONE"],
11551
+ "Run the missing verification and attach outcome plus post-action evidence before marking done."
11552
+ );
11553
+ }
11554
+ function evaluateModelWorthiness(point, ctx) {
11555
+ if (point !== "before_task_start") return null;
11556
+ if (!ctx.expensiveModelRequested || !ctx.taskSeemsTrivial) return null;
11557
+ return buildTrigger(
11558
+ "model_worthiness_guard",
11559
+ point,
11560
+ "WARN",
11561
+ "This task looks trivial relative to the requested review depth.",
11562
+ ["MODEL_WORTHINESS_WARN"],
11563
+ "Use a lighter-weight path unless deeper review is actually needed."
11564
+ );
11565
+ }
11566
+ function finalVerdict(triggers) {
11567
+ if (triggers.length === 0) return "NOOP";
11568
+ for (const verdict of VERDICT_PRECEDENCE) {
11569
+ if (triggers.some((trigger) => trigger.verdict === verdict)) return verdict;
11570
+ }
11571
+ return "NOOP";
11572
+ }
11573
+ function listWorkControls() {
11574
+ return BUILTIN_CONTROLS.map((control) => ({ ...control, enforcementPoints: [...control.enforcementPoints], packaging: { ...control.packaging }, dataPersisted: [...control.dataPersisted], testsRequired: [...control.testsRequired] }));
11575
+ }
11576
+ function getWorkControl(controlId) {
11577
+ const found = CONTROL_BY_ID.get(controlId);
11578
+ if (!found) return null;
11579
+ return {
11580
+ ...found,
11581
+ enforcementPoints: [...found.enforcementPoints],
11582
+ packaging: { ...found.packaging },
11583
+ dataPersisted: [...found.dataPersisted],
11584
+ testsRequired: [...found.testsRequired]
11585
+ };
11586
+ }
11587
+ function evaluateWorkControls(enforcementPoint, ctx) {
11588
+ const triggers = [
11589
+ evaluateSecretsGuard(enforcementPoint, ctx),
11590
+ evaluateScopeGuard(enforcementPoint, ctx),
11591
+ evaluateRiskyActionApproval(enforcementPoint, ctx),
11592
+ evaluateShellCommand(enforcementPoint, ctx),
11593
+ evaluateFileWrite(enforcementPoint, ctx),
11594
+ evaluateRelease(enforcementPoint, ctx),
11595
+ evaluateProof(enforcementPoint, ctx),
11596
+ evaluateModelWorthiness(enforcementPoint, ctx)
11597
+ ].filter((trigger) => trigger !== null);
11598
+ const verdict = finalVerdict(triggers);
11599
+ const reasonCodes = Array.from(new Set(triggers.flatMap((trigger) => trigger.reasonCodes)));
11600
+ const safeNextActions = Array.from(new Set(triggers.map((trigger) => trigger.nextAction).filter((value) => typeof value === "string" && value.length > 0)));
11601
+ const blockingTrigger = triggers.find((trigger) => trigger.verdict === verdict) ?? null;
11602
+ return {
11603
+ enforcementPoint,
11604
+ finalVerdict: verdict,
11605
+ finalReason: blockingTrigger?.reason ?? "No relevant work control fired.",
11606
+ reasonCodes,
11607
+ approvalRequired: triggers.some((trigger) => trigger.verdict === "ASK" || trigger.verdict === "ESCALATE"),
11608
+ proofRequired: triggers.some((trigger) => trigger.verdict === "REQUIRE_PROOF"),
11609
+ triggers,
11610
+ safeNextActions,
11611
+ deterministic: true
11612
+ };
11613
+ }
11614
+
11615
+ // src/avorelo/capabilities/runtime-flow/index.ts
11123
11616
  function runtimeDir(dir) {
11124
11617
  return join39(dir, ".avorelo", "runtime");
11125
11618
  }
@@ -11154,6 +11647,22 @@ function safeFallbackProjection(reasonCodes, extraVerifierPlan = []) {
11154
11647
  containsRawSecret: false
11155
11648
  };
11156
11649
  }
11650
+ function toRuntimeWorkControls(evaluation) {
11651
+ return {
11652
+ enforcementPoint: evaluation.enforcementPoint,
11653
+ finalVerdict: evaluation.finalVerdict,
11654
+ finalReason: evaluation.finalReason,
11655
+ reasonCodes: evaluation.reasonCodes,
11656
+ approvalRequired: evaluation.approvalRequired,
11657
+ proofRequired: evaluation.proofRequired,
11658
+ triggeredControls: evaluation.triggers.map((trigger) => ({
11659
+ controlId: trigger.controlId,
11660
+ verdict: trigger.verdict,
11661
+ reasonCodes: trigger.reasonCodes
11662
+ })),
11663
+ safeNextActions: evaluation.safeNextActions
11664
+ };
11665
+ }
11157
11666
  function freshRuntimeId(seed) {
11158
11667
  return "rts_" + createHash12("sha256").update(seed).digest("hex").slice(0, 12);
11159
11668
  }
@@ -11218,6 +11727,20 @@ function runRuntimeSession(input) {
11218
11727
  const routing = decideRouting({ task, dir, planTier });
11219
11728
  const c = routing.contract;
11220
11729
  const displayTask = routing.displayTask;
11730
+ const taskStartControls = evaluateWorkControls("before_task_start", {
11731
+ task: displayTask,
11732
+ planTier,
11733
+ route: c.route,
11734
+ riskClass: c.riskClass,
11735
+ proofTier: c.proofTier,
11736
+ approvalPolicy: c.approvalPolicy,
11737
+ safeRunDecision: c.safetyBoundary.safeRunDecision,
11738
+ secretRiskCodes: c.safetyBoundary.secretRiskCodes,
11739
+ userApproved: routing.gate === "allow",
11740
+ productionImpact: /deploy|release|publish|production|prod/i.test(displayTask),
11741
+ taskSeemsTrivial: c.route === "deterministic_only" && c.riskClass === "low",
11742
+ expensiveModelRequested: c.route === "deep_reasoning_required"
11743
+ });
11221
11744
  const layers = [{
11222
11745
  order: 1,
11223
11746
  layer: "safety_and_routing",
@@ -11245,6 +11768,7 @@ function runRuntimeSession(input) {
11245
11768
  safeRunDecision: c.safetyBoundary.safeRunDecision,
11246
11769
  secretRiskCodes: c.safetyBoundary.secretRiskCodes ?? []
11247
11770
  },
11771
+ workControls: toRuntimeWorkControls(taskStartControls),
11248
11772
  layers,
11249
11773
  redacted: true,
11250
11774
  containsRawSecret: false,
@@ -11809,6 +12333,8 @@ function buildRuntimeSessionSyncMetadata(record) {
11809
12333
  riskClass: record.riskClass,
11810
12334
  proofTier: record.proofTier,
11811
12335
  approvalPolicy: record.approvalPolicy,
12336
+ workControlVerdict: record.workControls.finalVerdict,
12337
+ workControlTriggerCount: record.workControls.triggeredControls.length,
11812
12338
  layerStatuses,
11813
12339
  secretRiskCodes: record.safetyBoundary.secretRiskCodes,
11814
12340
  canShowSavings: record.proof?.canShowSavings ?? false,
@@ -11824,6 +12350,8 @@ function validateRuntimeSession(record) {
11824
12350
  if (record.containsRawSecret !== false) reasons.push("contains_raw_secret");
11825
12351
  if (record.containsRawPrompt !== false) reasons.push("contains_raw_prompt");
11826
12352
  if (record.containsRawSourceDump !== false) reasons.push("contains_raw_source_dump");
12353
+ if (!record.workControls) reasons.push("work_controls_missing");
12354
+ else if (record.workControls.finalVerdict === "REQUIRE_PROOF" && record.gate === "allow") reasons.push("proof_required_task_started");
11827
12355
  if (record.gate === "blocked" && (record.session || record.context || record.continuity || record.tokenCost || record.proof || record.value || record.efficiencySync)) {
11828
12356
  reasons.push("blocked_gate_ran_downstream");
11829
12357
  }
@@ -11914,6 +12442,18 @@ function buildControlCenter(dir, opts) {
11914
12442
  } : { status: "unavailable" };
11915
12443
  if (runtime) sources.push(avoreloPath(dir, "runtime", "session.latest.json"));
11916
12444
  else notes.push('No runtime session yet \u2014 run `avorelo run "<task>"` to create one.');
12445
+ const workControlsSection = runtime?.workControls ? {
12446
+ status: "available",
12447
+ enforcementPoint: runtime.workControls.enforcementPoint,
12448
+ finalVerdict: runtime.workControls.finalVerdict,
12449
+ approvalRequired: runtime.workControls.approvalRequired,
12450
+ proofRequired: runtime.workControls.proofRequired,
12451
+ triggeredCount: runtime.workControls.triggeredControls.length,
12452
+ controls: runtime.workControls.triggeredControls.map((control) => ({
12453
+ controlId: control.controlId,
12454
+ verdict: control.verdict
12455
+ }))
12456
+ } : { status: "unavailable" };
11917
12457
  const contextPackSection = runtime?.contextPack ? {
11918
12458
  status: "available",
11919
12459
  contextPackId: runtime.contextPack.contextPackId,
@@ -12081,6 +12621,7 @@ function buildControlCenter(dir, opts) {
12081
12621
  workspace: dir,
12082
12622
  sections: {
12083
12623
  runtimeSession: runtimeSection,
12624
+ workControls: workControlsSection,
12084
12625
  contextPack: contextPackSection,
12085
12626
  proof: proofSection,
12086
12627
  value: valueSection,
@@ -12115,6 +12656,7 @@ function renderText2(m) {
12115
12656
  } else {
12116
12657
  lines.push(" Runtime: none yet");
12117
12658
  }
12659
+ lines.push(` Controls: ${s.workControls.status === "available" ? `${s.workControls.finalVerdict} at ${s.workControls.enforcementPoint} \xB7 approval=${s.workControls.approvalRequired} \xB7 proof=${s.workControls.proofRequired} \xB7 triggers=${s.workControls.triggeredCount}` : "none"}`);
12118
12660
  lines.push(` Ctx pack: ${s.contextPack.status === "available" ? `${s.contextPack.contextPackId} \xB7 consumer=${s.contextPack.consumer} adapter=${s.contextPack.selectedAdapter} \xB7 refs=${s.contextPack.allowedCount}/${s.contextPack.forbiddenCount} blocked \xB7 budget=${s.contextPack.budget} used=${s.contextPack.contextBudgetUsed} \xB7 tags=${s.contextPack.provenanceTagCount}` : "none"}`);
12119
12661
  lines.push(` Proof: ${s.proof.status === "available" ? `${s.proof.reportId} \xB7 savings ${s.proof.canShowSavings ? "shown" : `not claimed (${s.proof.savingsRefusalReason ?? "no_comparative_evidence"})`}` : "none"}`);
12120
12662
  lines.push(` Token/cost:${s.costEvidence.status === "available" ? ` ${s.costEvidence.confidence} \xB7 costSummary=${s.costEvidence.canShowCostSummary} \xB7 ${s.costEvidence.evidenceCount} item(s)` : " none"}`);
@@ -12182,6 +12724,7 @@ function renderHtml2(m) {
12182
12724
  <h1>Avorelo \u2014 Local Control Center</h1>
12183
12725
  <table>
12184
12726
  ${row("Runtime", s.runtimeSession.status === "available" ? `${esc2(s.runtimeSession.sessionStatus)} \xB7 gate=${esc2(s.runtimeSession.gate)} route=${esc2(s.runtimeSession.route)} risk=${esc2(s.runtimeSession.riskClass)} proof=${esc2(s.runtimeSession.proofTier)}<div>${layers}</div>` : '<span class="muted">none yet</span>')}
12727
+ ${row("Controls", s.workControls.status === "available" ? `${esc2(s.workControls.finalVerdict)} \xB7 point=${esc2(s.workControls.enforcementPoint)} \xB7 approval=${esc2(s.workControls.approvalRequired)} \xB7 proof=${esc2(s.workControls.proofRequired)} \xB7 triggers=${esc2(s.workControls.triggeredCount)}${(s.workControls.controls ?? []).length ? `<div class="muted">${(s.workControls.controls ?? []).map((c) => `${esc2(c.controlId)}=${esc2(c.verdict)}`).join(" \xB7 ")}</div>` : ""}` : '<span class="muted">none</span>')}
12185
12728
  ${row("Ctx pack", s.contextPack.status === "available" ? `${esc2(s.contextPack.contextPackId)} \xB7 consumer=${esc2(s.contextPack.consumer)} adapter=${esc2(s.contextPack.selectedAdapter)} \xB7 refs=${esc2(s.contextPack.allowedCount)}/${esc2(s.contextPack.forbiddenCount)} blocked \xB7 budget=${esc2(s.contextPack.budget)} used=${esc2(s.contextPack.contextBudgetUsed)} \xB7 tags=${esc2(s.contextPack.provenanceTagCount)}` : '<span class="muted">none</span>')}
12186
12729
  ${row("Proof", s.proof.status === "available" ? `${esc2(s.proof.reportId)} \xB7 savings ${s.proof.canShowSavings ? "shown" : `<b>not claimed</b> (${esc2(s.proof.savingsRefusalReason ?? "no_comparative_evidence")})`}` : '<span class="muted">none</span>')}
12187
12730
  ${row("Token/cost", s.costEvidence.status === "available" ? `${esc2(s.costEvidence.confidence)} \xB7 costSummary=${esc2(s.costEvidence.canShowCostSummary)} \xB7 ${esc2(s.costEvidence.evidenceCount)} item(s)` : '<span class="muted">none</span>')}
@@ -13013,7 +13556,7 @@ function checkEnvironmentIntegrity(dir, signals) {
13013
13556
  if (signals?.staleProcess) reasonCodes.push("STALE_PROCESS");
13014
13557
  return { compromised: reasonCodes.length > 0, reasonCodes };
13015
13558
  }
13016
- function evaluateProof(input) {
13559
+ function evaluateProof2(input) {
13017
13560
  const declared = input.artifacts ?? [];
13018
13561
  const readbacks = (input.readbacks ?? []).map((c) => readBack(input.dir, c));
13019
13562
  const readbackArtifacts = readbacks.map((r2) => r2.artifact).filter((a) => a !== null);
@@ -16088,6 +16631,9 @@ var RUNTIME_ACTIONS = [
16088
16631
  { action: "cli.status", capabilityKey: "session_value_visible", minimumTier: "free", surface: "cli", fallback: "allow", reason: "Show activation status", freeFallbackExists: true },
16089
16632
  { action: "cli.open", capabilityKey: "session_value_visible", minimumTier: "free", surface: "cli", fallback: "allow", reason: "Local receipts dashboard", freeFallbackExists: true },
16090
16633
  { action: "cli.control_center", capabilityKey: "session_value_visible", minimumTier: "free", surface: "cli", fallback: "allow", reason: "Local Control Center", freeFallbackExists: true },
16634
+ { action: "cli.controls", capabilityKey: "session_value_visible", minimumTier: "free", surface: "cli", fallback: "allow", reason: "Inspect built-in Work Controls", freeFallbackExists: true },
16635
+ { action: "cli.receipt", capabilityKey: "session_proof_summary", minimumTier: "free", surface: "cli", fallback: "allow", reason: "Inspect latest local receipt", freeFallbackExists: true },
16636
+ { action: "cli.proof", capabilityKey: "session_proof_summary", minimumTier: "free", surface: "cli", fallback: "allow", reason: "Validate latest proof status", freeFallbackExists: true },
16091
16637
  { action: "cli.doctor", capabilityKey: "scope_safety_check_basic", minimumTier: "free", surface: "cli", fallback: "allow", reason: "Health check", freeFallbackExists: true },
16092
16638
  { action: "cli.verify", capabilityKey: "session_proof_summary", minimumTier: "free", surface: "cli", fallback: "allow", reason: "Validate activation state", freeFallbackExists: true },
16093
16639
  { action: "cli.billing", capabilityKey: "session_value_visible", minimumTier: "free", surface: "cli", fallback: "allow", reason: "Billing readiness (read-only)", freeFallbackExists: true },
@@ -16126,6 +16672,9 @@ var FREE_COMMANDS = /* @__PURE__ */ new Set([
16126
16672
  "status",
16127
16673
  "open",
16128
16674
  "control-center",
16675
+ "controls",
16676
+ "receipt",
16677
+ "proof",
16129
16678
  "doctor",
16130
16679
  "verify",
16131
16680
  "billing",
@@ -17241,6 +17790,10 @@ function help() {
17241
17790
  " status [--target <dir>] Show activation and session status",
17242
17791
  " open [--target <dir>] [--format html|json|text] Local receipts dashboard",
17243
17792
  " control-center [--target <dir>] [--format html|json|text] Local Control Center (read-only, all local state)",
17793
+ " controls list [--json] List built-in Work Controls",
17794
+ " controls explain <control> [--json] Explain one built-in Work Control",
17795
+ " receipt latest [--target <dir>] [--json] Show the latest local receipt",
17796
+ " proof check [--target <dir>] [--json] Validate the latest proof status",
17244
17797
  " doctor [--target <dir>] Health check (adapters, hooks, session)",
17245
17798
  " verify [--target <dir>] Validate activation state invariants",
17246
17799
  " uninstall [--target <dir>] Remove all Avorelo-managed content",
@@ -17721,6 +18274,140 @@ function cmdControlCenter(args) {
17721
18274
  process.stdout.write(JSON.stringify({ ok: true, htmlPath, runtime: model.sections.runtimeSession.status, sources: model.sources.length }, null, 2) + "\n");
17722
18275
  return 0;
17723
18276
  }
18277
+ function cmdControls(args) {
18278
+ const sub = args[0] && !args[0].startsWith("--") ? args[0] : "list";
18279
+ const asJson = args.includes("--json");
18280
+ if (sub === "list") {
18281
+ const controls = listWorkControls();
18282
+ if (asJson) {
18283
+ process.stdout.write(JSON.stringify(controls, null, 2) + "\n");
18284
+ return 0;
18285
+ }
18286
+ process.stdout.write([
18287
+ "",
18288
+ "Avorelo Work Controls",
18289
+ ...controls.map((control) => ` ${control.controlId}: ${control.uiCopy} [${control.defaultState}]`),
18290
+ ""
18291
+ ].join("\n"));
18292
+ return 0;
18293
+ }
18294
+ if (sub === "explain") {
18295
+ const controlId = args.find((value, index) => index > 0 && !value.startsWith("--"));
18296
+ if (!controlId) {
18297
+ process.stderr.write("Usage: avorelo controls explain <control> [--json]\n");
18298
+ return 2;
18299
+ }
18300
+ const control = getWorkControl(controlId);
18301
+ if (!control) {
18302
+ process.stderr.write(`Unknown Work Control: ${controlId}
18303
+ `);
18304
+ return 1;
18305
+ }
18306
+ if (asJson) {
18307
+ process.stdout.write(JSON.stringify(control, null, 2) + "\n");
18308
+ return 0;
18309
+ }
18310
+ process.stdout.write([
18311
+ "",
18312
+ `${control.title} (${control.controlId})`,
18313
+ ` Purpose: ${control.purpose}`,
18314
+ ` Points: ${control.enforcementPoints.join(", ")}`,
18315
+ ` Default: ${control.defaultState}`,
18316
+ ` Packaging: Free=${control.packaging.free} Pro=${control.packaging.pro} Teams=${control.packaging.teams}`,
18317
+ ` Local: ${control.localOnlyBehavior}`,
18318
+ ` Cloud: ${control.cloudBehavior}`,
18319
+ ` Persisted: ${control.dataPersisted.join(", ")}`,
18320
+ ` Tests: ${control.testsRequired.join(", ")}`,
18321
+ ` UI copy: ${control.uiCopy}`,
18322
+ ""
18323
+ ].join("\n"));
18324
+ return 0;
18325
+ }
18326
+ process.stderr.write("Usage: avorelo controls <list|explain> [<control>] [--json]\n");
18327
+ return 2;
18328
+ }
18329
+ function latestReceiptFor(dir) {
18330
+ return listReceipts(dir).sort((a, b) => {
18331
+ const aw = typeof a.writtenAt === "number" ? a.writtenAt : -1;
18332
+ const bw = typeof b.writtenAt === "number" ? b.writtenAt : -1;
18333
+ if (aw !== bw) return bw - aw;
18334
+ return b.receiptId.localeCompare(a.receiptId);
18335
+ })[0] ?? null;
18336
+ }
18337
+ function cmdReceipt(args) {
18338
+ const sub = args[0] && !args[0].startsWith("--") ? args[0] : "latest";
18339
+ const target = arg(args, "--target", process.cwd());
18340
+ const asJson = args.includes("--json");
18341
+ if (sub !== "latest") {
18342
+ process.stderr.write("Usage: avorelo receipt latest [--target <dir>] [--json]\n");
18343
+ return 2;
18344
+ }
18345
+ const latest = latestReceiptFor(target);
18346
+ if (!latest) {
18347
+ if (asJson) {
18348
+ process.stdout.write(JSON.stringify({ status: "none", message: "no receipts found" }, null, 2) + "\n");
18349
+ return 0;
18350
+ }
18351
+ process.stdout.write("\nNo receipts found yet. Run a controlled session first.\n\n");
18352
+ return 0;
18353
+ }
18354
+ const full = readReceipt(target, latest.receiptId) ?? latest;
18355
+ if (asJson) {
18356
+ process.stdout.write(JSON.stringify(full, null, 2) + "\n");
18357
+ return 0;
18358
+ }
18359
+ process.stdout.write([
18360
+ "",
18361
+ `Latest receipt: ${full.receiptId}`,
18362
+ ` Contract: ${full.contractId}`,
18363
+ ` Decision: ${full.decision}`,
18364
+ ` Evidence: ${full.evidenceLevels.join(", ") || "none"}`,
18365
+ ` Reasons: ${full.decisionBasis.reasonCodes.join(", ") || "none"}`,
18366
+ ` Next: ${full.safeNextActions.join("; ") || "none"}`,
18367
+ ""
18368
+ ].join("\n"));
18369
+ return 0;
18370
+ }
18371
+ function cmdProof(args) {
18372
+ const sub = args[0] && !args[0].startsWith("--") ? args[0] : "check";
18373
+ const target = arg(args, "--target", process.cwd());
18374
+ const asJson = args.includes("--json");
18375
+ if (sub !== "check") {
18376
+ process.stderr.write("Usage: avorelo proof check [--target <dir>] [--json]\n");
18377
+ return 2;
18378
+ }
18379
+ const report = loadLatestProofReport(target) ?? buildProofReportFromLocalEvidence(target);
18380
+ const summary = summarizeProofReport(report);
18381
+ const validation = validateProofReport(report);
18382
+ const payload = {
18383
+ reportId: report.reportId,
18384
+ valid: validation.valid,
18385
+ reasons: validation.reasons,
18386
+ canShowCostSummary: summary.canShowCostSummary,
18387
+ canShowSavings: summary.canShowSavings,
18388
+ savingsRefusalReason: summary.savingsRefusalReason,
18389
+ sections: summary.sections
18390
+ };
18391
+ if (asJson) {
18392
+ process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
18393
+ return validation.valid ? 0 : 1;
18394
+ }
18395
+ process.stdout.write([
18396
+ "",
18397
+ `Proof check: ${report.reportId}`,
18398
+ ` Valid: ${validation.valid}`,
18399
+ ` Found: ${summary.sections.found}`,
18400
+ ` Protected: ${summary.sections.protected}`,
18401
+ ` Verified: ${summary.sections.verified}`,
18402
+ ` Attention: ${summary.sections.needsAttention}`,
18403
+ ` Next: ${summary.sections.next}`,
18404
+ ` Cost: ${summary.canShowCostSummary ? "available" : "unavailable"}`,
18405
+ ` Savings: ${summary.canShowSavings ? "shown" : "not claimed"}${summary.savingsRefusalReason ? ` (${summary.savingsRefusalReason})` : ""}`,
18406
+ validation.reasons.length ? ` Issues: ${validation.reasons.join(", ")}` : " Issues: none",
18407
+ ""
18408
+ ].join("\n"));
18409
+ return validation.valid ? 0 : 1;
18410
+ }
17724
18411
  function cmdVerify(args) {
17725
18412
  const target = arg(args, "--target", process.cwd());
17726
18413
  const stateVerify = verifyActivationState(target);
@@ -17734,7 +18421,7 @@ function cmdVerify(args) {
17734
18421
  const input = loadProofInput(target);
17735
18422
  if (input) {
17736
18423
  const contract = createWorkContract({ contractId: "verify", objective: input.objective ?? "verify real-workflow proof", allowedPaths: [join61(target, "src")], planTier: "Free" });
17737
- const r2 = evaluateProof({ contract, artifacts: input.artifacts, readbacks: input.readbacks, dir: target, sampleSize: input.sampleSize, receiptId: "rcpt_verify" });
18424
+ const r2 = evaluateProof2({ contract, artifacts: input.artifacts, readbacks: input.readbacks, dir: target, sampleSize: input.sampleSize, receiptId: "rcpt_verify" });
17738
18425
  process.stdout.write(JSON.stringify({ scope: "full", activationState: { valid: true }, proof: { decision: r2.decision, confidence: r2.confidence } }, null, 2) + "\n");
17739
18426
  return r2.decision === "STOP_DONE" ? 0 : 1;
17740
18427
  }
@@ -19722,6 +20409,12 @@ function main(argv) {
19722
20409
  return cmdOpen(rest);
19723
20410
  case "control-center":
19724
20411
  return cmdControlCenter(rest);
20412
+ case "controls":
20413
+ return cmdControls(rest);
20414
+ case "receipt":
20415
+ return cmdReceipt(rest);
20416
+ case "proof":
20417
+ return cmdProof(rest);
19725
20418
  case "verify":
19726
20419
  return cmdVerify(rest);
19727
20420
  case "site":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "avorelo",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "description": "Avorelo — AI Work Control. One command. Keep using your AI coding tool. Avorelo quietly keeps the work focused and saves proof.",