review-council 3.0.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +150 -1
  2. package/dist/config/harness.d.ts +9 -0
  3. package/dist/config/harness.js +3 -2
  4. package/dist/config/harness.js.map +1 -1
  5. package/dist/evidence/format.d.ts +19 -0
  6. package/dist/evidence/format.js +109 -0
  7. package/dist/evidence/format.js.map +1 -0
  8. package/dist/evidence/reads.d.ts +11 -0
  9. package/dist/evidence/reads.js +16 -0
  10. package/dist/evidence/reads.js.map +1 -0
  11. package/dist/evidence/show.d.ts +10 -0
  12. package/dist/evidence/show.js +41 -0
  13. package/dist/evidence/show.js.map +1 -0
  14. package/dist/evidence/status.d.ts +37 -0
  15. package/dist/evidence/status.js +72 -0
  16. package/dist/evidence/status.js.map +1 -0
  17. package/dist/evidence/target.d.ts +17 -0
  18. package/dist/evidence/target.js +74 -0
  19. package/dist/evidence/target.js.map +1 -0
  20. package/dist/evidence/types.d.ts +152 -0
  21. package/dist/evidence/types.js +94 -0
  22. package/dist/evidence/types.js.map +1 -0
  23. package/dist/index.js +272 -32
  24. package/dist/index.js.map +1 -1
  25. package/dist/models/server-stats.d.ts +80 -0
  26. package/dist/models/server-stats.js +109 -0
  27. package/dist/models/server-stats.js.map +1 -0
  28. package/dist/report/run-header.d.ts +2 -0
  29. package/dist/report/run-header.js.map +1 -1
  30. package/dist/report/uuid.d.ts +9 -0
  31. package/dist/report/uuid.js +19 -1
  32. package/dist/report/uuid.js.map +1 -1
  33. package/dist/resolver/github.d.ts +11 -0
  34. package/dist/resolver/github.js +21 -0
  35. package/dist/resolver/github.js.map +1 -1
  36. package/dist/resolver/target.d.ts +8 -0
  37. package/dist/resolver/target.js +45 -0
  38. package/dist/resolver/target.js.map +1 -1
  39. package/dist/telemetry/attest.d.ts +79 -0
  40. package/dist/telemetry/attest.js +269 -0
  41. package/dist/telemetry/attest.js.map +1 -0
  42. package/dist/telemetry/backfill.d.ts +86 -0
  43. package/dist/telemetry/backfill.js +446 -0
  44. package/dist/telemetry/backfill.js.map +1 -0
  45. package/dist/telemetry/credentials.d.ts +2 -1
  46. package/dist/telemetry/credentials.js.map +1 -1
  47. package/dist/telemetry/deliver.d.ts +15 -2
  48. package/dist/telemetry/deliver.js +32 -4
  49. package/dist/telemetry/deliver.js.map +1 -1
  50. package/dist/telemetry/events.d.ts +25 -0
  51. package/dist/telemetry/events.js +26 -0
  52. package/dist/telemetry/events.js.map +1 -1
  53. package/dist/telemetry/read-sink.d.ts +27 -0
  54. package/dist/telemetry/read-sink.js +23 -0
  55. package/dist/telemetry/read-sink.js.map +1 -0
  56. package/dist/telemetry/sink.d.ts +21 -2
  57. package/dist/telemetry/sink.js +19 -4
  58. package/dist/telemetry/sink.js.map +1 -1
  59. package/package.json +1 -1
package/README.md CHANGED
@@ -91,6 +91,7 @@ Review a PR, a local diff, or uncommitted work.
91
91
  | `--expect-head-sha <sha>` | Fail fast unless the resolved head commit equals this SHA |
92
92
  | `--spec-source <source>` | Where `--spec` came from: `flag`, `repo_file`, or `harness_issue:<ID>` |
93
93
  | `--converge-target <key>` / `--round <n>` / `--attempt <n>` | Converge context recorded in the report (or `RCL_CONVERGE_TARGET` / `_ROUND` / `_ATTEMPT`) |
94
+ | `--attest` | GitHub Actions gate workflow only: exchange the job's OIDC token for a run-bound Harness credential and record the review as attested (see below) |
94
95
  | `--config <path>` | Path to a config file |
95
96
 
96
97
  `--role`, `--roles`, and `--reviewer` are mutually exclusive. So are a positional target, `--staged`, and `--working-tree` — pick exactly one review source. Untracked files are invisible to `git diff` and therefore not reviewed.
@@ -292,6 +293,116 @@ harness:
292
293
 
293
294
  ---
294
295
 
296
+ ### `--attest`: attested reviews from the gate workflow
297
+
298
+ Only evidence recorded from the organization's own gate workflow on GitHub
299
+ Actions counts for the enforced gate (epic IO-12475, section 4.1). Inside
300
+ such a job — one that grants `id-token: write` — `rcl review owner/repo#N
301
+ --attest` asks the runner for the job's OIDC token with the Harness origin as
302
+ audience, exchanges it at `POST /api/v1/reviews/attest` for a **run-bound
303
+ credential** (`rbc_…`, thirty minutes, one rcl run id, valid while the
304
+ Actions run is in progress), and records the review under it: the envelope,
305
+ its artifacts, the model keys and the model stats all travel with that
306
+ credential and nothing else. Harness verifies the token, requires the
307
+ workflow file to be on the organization's gate allow-list at its default
308
+ branch, re-reads the pull request through its GitHub App and stores the run
309
+ only if the reviewed head is the pull request's current head and the PR is
310
+ not from a fork — the run is then `credential_kind: attested`.
311
+
312
+ `--attest` fails loudly, before any token is requested or any reviewer is
313
+ paid: outside Actions (no `ACTIONS_ID_TOKEN_REQUEST_URL` / `_TOKEN`), without
314
+ `HARNESS_API_URL`, off a pull request target, with a telemetry level other
315
+ than `full` (from `RCL_TELEMETRY` or the project config — an attested run
316
+ carries its full report), or when Harness refuses the exchange (the refusal
317
+ names the reason: `workflow_not_allowed`, `reviews_disabled`,
318
+ `run_not_in_progress`, …). It never falls back to `HARNESS_API_TOKEN` or the
319
+ stored login, it implies `--evidence-required`, and nothing recorded under the
320
+ run-bound credential is ever spooled — the credential does not outlive the
321
+ workflow run. A review that outlasts most of the credential's thirty minutes
322
+ mints it again for the same run id before delivery. Pair it with
323
+ `--expect-head-sha` so a moved pull request fails fast instead of being
324
+ refused at ingest.
325
+
326
+ ```yaml
327
+ # .github/workflows/review_gate.yml (dispatched by Harness for one pull request)
328
+ permissions:
329
+ id-token: write
330
+ contents: read
331
+ jobs:
332
+ review:
333
+ runs-on: ubuntu-latest
334
+ env:
335
+ HARNESS_API_URL: https://harness.infra.one
336
+ steps:
337
+ - run: npm i -g review-council
338
+ # Inputs reach the shell through the environment, never by expression
339
+ # interpolation into the command line.
340
+ - env:
341
+ REPO: ${{ inputs.repo }}
342
+ PR: ${{ inputs.pr }}
343
+ HEAD_SHA: ${{ inputs.head_sha }}
344
+ run: rcl review "$REPO#$PR" --attest --expect-head-sha "$HEAD_SHA" --ci
345
+ ```
346
+
347
+ ---
348
+
349
+ ### `rcl evidence status` and `rcl evidence show`
350
+
351
+ What Harness holds — never the client's own claim. `rcl evidence status`
352
+ prints the gate status Harness computed for a pull request: its known head,
353
+ the advisory and enforced projections (status, the rounds behind them, the
354
+ actionable findings still open) and the merge decision once it merged. The
355
+ exit code is the contract the skills gate on:
356
+
357
+ | exit | meaning |
358
+ | --- | --- |
359
+ | 0 | the judged projection (advisory by default, `--enforced` on request) is `converged` |
360
+ | 1 | any other status: `none`, `stale`, `unverified`, `inconclusive`, `fixes_pending`, `unresolved` |
361
+ | 2 | the pull request could not be named |
362
+ | 3 | the read could not be answered: no credential, evidence off for the organization, unknown pull request, refused credential, unreachable host — never reported as "not converged" |
363
+
364
+ `rcl evidence show <run id>` prints one recorded run: header and verification,
365
+ credential tier and runner, reviewer health, artifact state, and every finding
366
+ with its identity, gating reason and triage verdict (`—` until the server
367
+ joins the verdict onto the finding).
368
+
369
+ The reads use the same credential rules as delivery — the stored `harness
370
+ login`, or `HARNESS_API_TOKEN` + `HARNESS_API_URL` in CI, the token sent to
371
+ its own host only — and need `reviews:read`. They do not depend on the
372
+ telemetry level: switching delivery off does not blind them.
373
+
374
+ ```bash
375
+ rcl evidence status # error: name the pull request
376
+ rcl evidence status 8524 # against the current checkout's origin remote
377
+ rcl evidence status '#8524' --enforced
378
+ rcl evidence status allocator-one/rcl#42 --json
379
+ rcl evidence status https://github.com/allocator-one/rcl/pull/42
380
+ rcl evidence show 01a08032-0838-76db-ade3-1990f6e54072
381
+ ```
382
+
383
+ ### `--for-pr` on a patch-file review
384
+
385
+ A patch-file review (`rcl review changes.patch`) carries no repository or pull
386
+ request, so Harness records it as a `patch` run it cannot verify or count for
387
+ any gate. `--for-pr owner/repo#N` (or a pull request URL) names the pull
388
+ request the patch was taken from (`RCL_FOR_PR` in the environment does the
389
+ same for patch files): the run is bound to that pull request and its
390
+ `--head-sha` — required with the flag — is verified against the pull
391
+ request's head. Owner and repository are lower-cased, as GitHub reads them. Counting the round for that pull request's
392
+ gate is the server half (IO-12585); until it lands, `rcl evidence status`
393
+ still reads `stale`/`none` for patch-file loops. The flag is refused on PR and
394
+ git-mode targets, which name their own pull request or checkout. `rcl-converge` passes `--converge-target`, `--round`
395
+ and `--attempt` on every round and adds `--for-pr` when a pull request loop
396
+ reviews a patch file taken from the pull request (a pull request target names
397
+ its own). A converge
398
+ target of the `owner/repo#N` form attributes the run the same way; a slug such
399
+ as `rcl-7` does not.
400
+
401
+ ```bash
402
+ rcl review round-3.patch --head-sha "$HEAD" --base-sha "$BASE" --for-pr allocator-one/rcl#42 \
403
+ --converge-target allocator-one/rcl#42 --round 3 --attempt 3 --evidence-required
404
+ ```
405
+
295
406
  ### `rcl models`
296
407
 
297
408
  The tool's own memory of which reviewers earn their seat. Every reviewer call
@@ -316,6 +427,43 @@ rcl models seed --from ~/recovered-rcl-artifacts # backfill from reports + con
316
427
 
317
428
  ---
318
429
 
430
+ Since 3.1 the table merges the organization's window from Harness
431
+ (`GET /api/v1/reviews/model-stats`, the server-side `rcl models` over every run
432
+ the org recorded, backfilled history included) with this machine's store: for a
433
+ model the server holds at least 20 outcomes for, the server's weight is used
434
+ (`source: server`); below that the local store decides (`local`); a model
435
+ neither knows enough about keeps the neutral weight (`neutral`). Reviews weight
436
+ consensus the same way, asking the server with a three-second bound and falling
437
+ back to the local store when it cannot answer. `--local` shows this machine's
438
+ view alone; `--json` carries `server` (host, window, rows) and `weights` with
439
+ their `source`.
440
+
441
+ ```bash
442
+ rcl models # org-wide where Harness has enough history, local otherwise
443
+ rcl models show --local # this machine's store only
444
+ rcl models show --window 30 --json
445
+ ```
446
+
447
+ ### `rcl telemetry backfill`
448
+
449
+ Recovered history becomes day-one evidence on Harness. `rcl telemetry backfill
450
+ --from <dir> --repo <owner/repo>` reads the pre-3.0 `rcl-report-*.json`
451
+ reports and `rcl-converge-*-ledger.md` ledgers in a directory (the same layout
452
+ `rcl models seed` reads) and posts each report as a run with `provenance:
453
+ backfill`: a synthesized header bound to the named repository (target `patch`,
454
+ the report bytes as the digest, a runner claim naming this command), the
455
+ report's findings with their stable identities, its reviewer calls, and the
456
+ report files as artifacts. Ledger bullets matched to a round's findings become
457
+ `verdicts_recorded` events. Run ids are UUIDv5 of `(host, repo, sha256 of the
458
+ report)` and event ids derive from them, so running the backfill twice reports
459
+ the second run as `0 new` — nothing is duplicated. Backfilled runs count for
460
+ model stats and analytics and never enter a gate decision.
461
+
462
+ ```bash
463
+ rcl telemetry backfill --from ~/recovered-rcl-artifacts --repo allocator-one/allocator-one --dry-run
464
+ rcl telemetry backfill --from ~/recovered-rcl-artifacts --repo allocator-one/allocator-one
465
+ ```
466
+
319
467
  ## Config File
320
468
 
321
469
  Place `.review-council.yml` in your project root (or any parent directory). All fields are optional.
@@ -446,7 +594,8 @@ For the full algorithm, see [CONSENSUS_V2_SPEC.md](./CONSENSUS_V2_SPEC.md).
446
594
  | `RCL_NO_HARNESS_KEYS` | Set to any value to disable Harness key distribution (below) |
447
595
  | `RCL_TELEMETRY` | `off` keeps every review on the machine (see `rcl telemetry`) |
448
596
  | `HARNESS_API_TOKEN` | CI credential for evidence delivery; requires `HARNESS_API_URL` — never pairs with the stored login host |
449
- | `HARNESS_API_URL` | The Harness host `HARNESS_API_TOKEN` was minted by |
597
+ | `HARNESS_API_URL` | The Harness host `HARNESS_API_TOKEN` was minted by; under `--attest` the host attested to (no token needed) |
598
+ | `ACTIONS_ID_TOKEN_REQUEST_URL` / `ACTIONS_ID_TOKEN_REQUEST_TOKEN` | Set by the GitHub Actions runner for jobs with `id-token: write`; `--attest` reads them and refuses to run without them |
450
599
 
451
600
  The default blocking council is direct-API only (Anthropic, OpenAI, Google) —
452
601
  no default review round ever waits on an OpenRouter-routed call. The default
@@ -10,6 +10,15 @@ export interface HarnessKeyOptions {
10
10
  env?: Record<string, string | undefined>;
11
11
  credentialsPath?: string;
12
12
  fetchImpl?: typeof fetch;
13
+ /**
14
+ * A credential already in hand — the run-bound one `rcl review --attest`
15
+ * minted (RCL-40). Used instead of the stored login, and the attestation
16
+ * stands in for the repository's `.harness-cli/config.json`.
17
+ */
18
+ credential?: {
19
+ url: string;
20
+ token: string;
21
+ };
13
22
  }
14
23
  /** Walk up from `startDir` looking for `.harness-cli/config.json`. */
15
24
  export declare function findHarnessRepoConfig(startDir: string): string | null;
@@ -121,9 +121,10 @@ export async function applyHarnessModelKeys(options = {}) {
121
121
  const missing = missingProviders(env);
122
122
  if (missing.length === 0)
123
123
  return { injected: [] };
124
- if (findHarnessRepoConfig(options.cwd ?? process.cwd()) === null)
124
+ if (options.credential === undefined && findHarnessRepoConfig(options.cwd ?? process.cwd()) === null)
125
125
  return { injected: [] };
126
- const credentials = await readStoredCredentials(options.credentialsPath ?? defaultCredentialsPath(env));
126
+ const credentials = options.credential ??
127
+ (await readStoredCredentials(options.credentialsPath ?? defaultCredentialsPath(env)));
127
128
  if (credentials === null) {
128
129
  return {
129
130
  injected: [],
@@ -1 +1 @@
1
- {"version":3,"file":"harness.js","sourceRoot":"","sources":["../../src/config/harness.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"harness.js","sourceRoot":"","sources":["../../src/config/harness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { GateStatus, RunDetail } from './types.js';
2
+ /**
3
+ * Terminal rendering of what Harness holds; one line per entry, no colour,
4
+ * safe to grep. Every server-supplied string — repository names, titles,
5
+ * paths, model names, errors, URLs — can trace back to reviewed code or model
6
+ * output, so it reaches the terminal only through `text()`: control and
7
+ * escape characters (C0, DEL, C1) become spaces, secrets are scrubbed, and
8
+ * the length is bounded, which also keeps every entry on its one line.
9
+ */
10
+ export declare function text(value: unknown, limit?: number): string;
11
+ /**
12
+ * `JSON.stringify` escapes C0 controls but writes DEL and the C1 range
13
+ * (U+007F–U+009F — 8-bit CSI and OSC introducers) literally; a title carrying
14
+ * one would reach the terminal or a CI log intact. They become `\\u00xx`
15
+ * escapes, which parse back to the same string.
16
+ */
17
+ export declare function safeJson(value: unknown): string;
18
+ export declare function formatGateStatus(status: GateStatus, judged: 'advisory' | 'enforced'): string[];
19
+ export declare function formatRun(run: RunDetail): string[];
@@ -0,0 +1,109 @@
1
+ import { scrubText } from '../telemetry/scrub.js';
2
+ /**
3
+ * Terminal rendering of what Harness holds; one line per entry, no colour,
4
+ * safe to grep. Every server-supplied string — repository names, titles,
5
+ * paths, model names, errors, URLs — can trace back to reviewed code or model
6
+ * output, so it reaches the terminal only through `text()`: control and
7
+ * escape characters (C0, DEL, C1) become spaces, secrets are scrubbed, and
8
+ * the length is bounded, which also keeps every entry on its one line.
9
+ */
10
+ export function text(value, limit = 300) {
11
+ if (value === null || value === undefined)
12
+ return '—';
13
+ return scrubText(String(value).replace(/[\u0000-\u001f\u007f-\u009f]/g, ' '), limit);
14
+ }
15
+ /**
16
+ * `JSON.stringify` escapes C0 controls but writes DEL and the C1 range
17
+ * (U+007F–U+009F — 8-bit CSI and OSC introducers) literally; a title carrying
18
+ * one would reach the terminal or a CI log intact. They become `\\u00xx`
19
+ * escapes, which parse back to the same string.
20
+ */
21
+ export function safeJson(value) {
22
+ return JSON.stringify(value, null, 2).replace(/[\u007f-\u009f]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
23
+ }
24
+ function short(sha) {
25
+ return typeof sha === 'string' && sha.length > 0 ? text(sha.slice(0, 10), 10) : '—';
26
+ }
27
+ function where(file, line) {
28
+ if (!file)
29
+ return '—';
30
+ return `${text(file)}${typeof line === 'number' ? `:${line}` : ''}`;
31
+ }
32
+ function projectionLines(name, projection, judged) {
33
+ const lines = [
34
+ `${name}: ${text(projection.status, 40)}${projection.conclusive === false ? ' (inconclusive)' : ''}${judged ? ' ← judged' : ''}` +
35
+ (projection.run_id ? ` — run ${short(projection.run_id)}${projection.run_url ? ` ${text(projection.run_url)}` : ''}` : ''),
36
+ ];
37
+ for (const round of projection.rounds) {
38
+ lines.push(` round ${typeof round.converge_round === 'number' ? round.converge_round : '·'}: ${short(round.id)} head ${short(round.head_sha)} ${text(round.tier, 40)}` +
39
+ `${round.received_at ? ` ${text(round.received_at, 40)}` : ''}${round.url ? ` ${text(round.url)}` : ''}`);
40
+ }
41
+ for (const finding of projection.actionable) {
42
+ lines.push(` actionable ${text(finding.identity_key ?? finding.ref ?? '?', 64)} ${text(finding.severity, 20)}/${text(finding.gating_reason, 20)} ` +
43
+ `${where(finding.file, finding.start_line)} ${text(finding.title, 200)}`);
44
+ }
45
+ return lines;
46
+ }
47
+ export function formatGateStatus(status, judged) {
48
+ const head = status.head;
49
+ const name = `${text(status.repo, 200)}#${text(status.pr_number, 20)}`;
50
+ const headLine = !head
51
+ ? `${name} — no head known to Harness`
52
+ : `${name} — head ${short(head.sha)}${head.source ? ` (${text(head.source, 40)})` : ''}` +
53
+ `${head.merged ? ` — merged${head.merge_commit_sha ? ` as ${short(head.merge_commit_sha)}` : ''}` : ''}` +
54
+ `${head.is_cross_repository ? ' — fork' : ''}`;
55
+ const decision = status.decision;
56
+ return [
57
+ headLine,
58
+ ...projectionLines('advisory', status.advisory, judged === 'advisory'),
59
+ ...projectionLines('enforced', status.enforced, judged === 'enforced'),
60
+ decision
61
+ ? `decision: ${text(decision.decision, 40)} at ${short(decision.reviewed_head_sha)} (${text(decision.source, 40)}` +
62
+ `${decision.merged_at ? `, merged ${text(decision.merged_at, 40)}` : ''})`
63
+ : 'decision: —',
64
+ ];
65
+ }
66
+ function reviewerHealth(run) {
67
+ const stats = run.stats ?? {};
68
+ const total = stats['totalReviews'];
69
+ const ok = stats['successfulReviews'];
70
+ if (typeof total === 'number' && typeof ok === 'number')
71
+ return `${ok}/${total}`;
72
+ return `${run.calls.filter((call) => call.status === 'success').length}/${run.calls.length}`;
73
+ }
74
+ export function formatRun(run) {
75
+ const runner = run.runner ?? {};
76
+ const runnerKind = typeof runner['kind'] === 'string' ? text(runner['kind'], 40) : '—';
77
+ const ciRunId = typeof runner['ci_run_id'] === 'string' ? ` ${text(runner['ci_run_id'], 40)}` : '';
78
+ const target = run.target;
79
+ const named = target.repo && target.pr_number ? `${text(target.repo, 200)}#${text(target.pr_number, 20)}` : text(target.kind, 40);
80
+ const converge = run.converge;
81
+ const lines = [
82
+ `run ${text(run.id, 64)}${run.url ? ` ${text(run.url)}` : ''}`,
83
+ `target ${named} @ ${short(target.head_sha)} (head ${text(run.head_verified ?? 'unknown', 20)}${run.repo_verified ? ', repo verified' : ''}` +
84
+ `${run.is_cross_repository ? ', fork' : ''})`,
85
+ `credential ${text(run.credential_kind, 40)} (${text(run.tier ?? 'asserted', 40)}) — runner ${runnerKind}${ciRunId} — rcl ${text(run.rcl_version, 40)}` +
86
+ (run.provenance && run.provenance !== 'live' ? ` — ${text(run.provenance, 40)}` : ''),
87
+ `reviewers ${reviewerHealth(run)} ok` +
88
+ (converge ? ` — converge ${text(converge.target, 200)}${typeof converge.round === 'number' ? ` round ${converge.round}` : ''}` : '') +
89
+ (run.received_at ? ` — received ${text(run.received_at, 40)}` : ''),
90
+ ];
91
+ for (const artifact of run.artifacts ?? []) {
92
+ lines.push(`artifact ${text(artifact.kind, 40)} ${artifact.stored ? 'stored' : 'missing'}${artifact.url ? ` ${text(artifact.url)}` : ''}`);
93
+ }
94
+ lines.push(`findings (${run.findings.length}): identity severity gating file:line title — verdict`);
95
+ for (const finding of run.findings) {
96
+ const verdict = finding.verdict
97
+ ? `${text(finding.verdict.verdict, 40)}${typeof finding.verdict.round === 'number' ? ` (round ${finding.verdict.round})` : ''}`
98
+ : '—';
99
+ lines.push(` ${text(finding.identity_key ?? finding.ref ?? '?', 64)} ${text(finding.severity, 20)} ${text(finding.gating_reason ?? 'none', 20)} ` +
100
+ `${where(finding.file, finding.start_line)} ${text(finding.title, 200)} — ${verdict}`);
101
+ }
102
+ lines.push(`calls (${run.calls.length}):`);
103
+ for (const call of run.calls) {
104
+ const duration = typeof call.duration_ms === 'number' ? ` ${call.duration_ms}ms` : '';
105
+ lines.push(` call ${text(call.model, 80)}${call.role ? ` ${text(call.role, 40)}` : ''} ${text(call.status, 20)}${duration}${call.error ? ` — ${text(call.error, 200)}` : ''}`);
106
+ }
107
+ return lines;
108
+ }
109
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/evidence/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { HarnessSink, RequestOptions, SinkOutcome } from '../telemetry/sink.js';
2
+ import { type GateStatus, type RunDetail } from './types.js';
3
+ /**
4
+ * The two evidence reads (epic IO-12475, section 9) on top of the sink's
5
+ * generic GET: each names what it asked for, so an answer about another pull
6
+ * request or run — or one shaped wrong — is refused, never trusted.
7
+ */
8
+ /** `GET /api/v1/reviews/prs/:owner/:repo/:number` — the gate status Harness computed for a pull request. */
9
+ export declare function getGateStatus(sink: HarnessSink, owner: string, repo: string, number: number, options?: RequestOptions): Promise<SinkOutcome<GateStatus>>;
10
+ /** `GET /api/v1/reviews/runs/:id` — one recorded run with its findings, calls and artifact state. */
11
+ export declare function getRun(sink: HarnessSink, id: string, options?: RequestOptions): Promise<SinkOutcome<RunDetail>>;
@@ -0,0 +1,16 @@
1
+ import { isGateStatus, isRunDetail } from './types.js';
2
+ /**
3
+ * The two evidence reads (epic IO-12475, section 9) on top of the sink's
4
+ * generic GET: each names what it asked for, so an answer about another pull
5
+ * request or run — or one shaped wrong — is refused, never trusted.
6
+ */
7
+ /** `GET /api/v1/reviews/prs/:owner/:repo/:number` — the gate status Harness computed for a pull request. */
8
+ export function getGateStatus(sink, owner, repo, number, options = {}) {
9
+ const path = `/api/v1/reviews/prs/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/${encodeURIComponent(String(number))}`;
10
+ return sink.getJson(path, (data) => (isGateStatus(data, `${owner}/${repo}`, number) ? data : null), options);
11
+ }
12
+ /** `GET /api/v1/reviews/runs/:id` — one recorded run with its findings, calls and artifact state. */
13
+ export function getRun(sink, id, options = {}) {
14
+ return sink.getJson(`/api/v1/reviews/runs/${encodeURIComponent(id)}`, (data) => (isRunDetail(data, id) ? data : null), options);
15
+ }
16
+ //# sourceMappingURL=reads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reads.js","sourceRoot":"","sources":["../../src/evidence/reads.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAmC,MAAM,YAAY,CAAC;AAExF;;;;GAIG;AAEH,4GAA4G;AAC5G,MAAM,UAAU,aAAa,CAC3B,IAAiB,EACjB,KAAa,EACb,IAAY,EACZ,MAAc,EACd,UAA0B,EAAE;IAE5B,MAAM,IAAI,GAAG,uBAAuB,kBAAkB,CAAC,KAAK,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;IAClI,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;AAC/G,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,MAAM,CAAC,IAAiB,EAAE,EAAU,EAAE,UAA0B,EAAE;IAChF,OAAO,IAAI,CAAC,OAAO,CAAC,wBAAwB,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;AAClI,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { type EvidenceDeps } from './status.js';
2
+ /**
3
+ * `rcl evidence show <run-id>` (RCL-41): one recorded run as Harness holds
4
+ * it — header, verification, reviewer health, artifact state, findings with
5
+ * their identity, gating reason and (once the server joins it) verdict.
6
+ * Exit 0 when the run was read (`EVIDENCE_EXIT.ok`), 2 without a run id, 3 when it could not be.
7
+ */
8
+ export declare function runEvidenceShow(runId: string, options: {
9
+ json?: boolean;
10
+ }, deps: EvidenceDeps): Promise<number>;
@@ -0,0 +1,41 @@
1
+ import { describeOutcome } from '../telemetry/sink.js';
2
+ import { formatRun, safeJson, text } from './format.js';
3
+ import { getRun } from './reads.js';
4
+ // Run ids are UUIDs (v7 live, v5 backfill); anything else never reaches the network or the terminal raw.
5
+ const RUN_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
6
+ import { EVIDENCE_EXIT, openSink } from './status.js';
7
+ /**
8
+ * `rcl evidence show <run-id>` (RCL-41): one recorded run as Harness holds
9
+ * it — header, verification, reviewer health, artifact state, findings with
10
+ * their identity, gating reason and (once the server joins it) verdict.
11
+ * Exit 0 when the run was read (`EVIDENCE_EXIT.ok`), 2 without a run id, 3 when it could not be.
12
+ */
13
+ export async function runEvidenceShow(runId, options, deps) {
14
+ // UUIDs are case-insensitive text; the server echoes the canonical lowercase form.
15
+ const id = (runId ?? '').trim().toLowerCase();
16
+ if (id === '') {
17
+ deps.stderr('Name the run id: `run.id` in a report, or the id in an `Evidence recorded:` URL.');
18
+ return EVIDENCE_EXIT.usage;
19
+ }
20
+ if (!RUN_ID.test(id)) {
21
+ deps.stderr(`Not a run id (a UUID): ${text(id, 80)}`);
22
+ return EVIDENCE_EXIT.usage;
23
+ }
24
+ const sink = await openSink(deps);
25
+ if (!sink)
26
+ return EVIDENCE_EXIT.unanswered;
27
+ const outcome = await getRun(sink, id);
28
+ if (outcome.kind !== 'ok') {
29
+ deps.stderr(`Cannot read run ${id}: ${describeOutcome(outcome)}`);
30
+ return EVIDENCE_EXIT.unanswered;
31
+ }
32
+ if (options.json) {
33
+ deps.stdout(safeJson(outcome.value));
34
+ }
35
+ else {
36
+ for (const line of formatRun(outcome.value))
37
+ deps.stdout(line);
38
+ }
39
+ return EVIDENCE_EXIT.ok;
40
+ }
41
+ //# sourceMappingURL=show.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"show.js","sourceRoot":"","sources":["../../src/evidence/show.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,yGAAyG;AACzG,MAAM,MAAM,GAAG,iEAAiE,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAEzE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAa,EAAE,OAA2B,EAAE,IAAkB;IAClG,mFAAmF;IACnF,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9C,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACd,IAAI,CAAC,MAAM,CAAC,kFAAkF,CAAC,CAAC;QAChG,OAAO,aAAa,CAAC,KAAK,CAAC;IAC7B,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QACrB,IAAI,CAAC,MAAM,CAAC,0BAA0B,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QACtD,OAAO,aAAa,CAAC,KAAK,CAAC;IAC7B,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI;QAAE,OAAO,aAAa,CAAC,UAAU,CAAC;IAE3C,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACvC,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,KAAK,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAClE,OAAO,aAAa,CAAC,UAAU,CAAC;IAClC,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACvC,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,aAAa,CAAC,EAAE,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { type HarnessSink } from '../telemetry/sink.js';
2
+ import { type RepoRef } from './target.js';
3
+ /**
4
+ * `rcl evidence status [<pr>]` (RCL-41): the gate status Harness computed for
5
+ * a pull request — never the client's own claim. The exit code is the
6
+ * contract the skills gate on: 0 only when the judged projection (advisory by
7
+ * default, `--enforced` on request) is `converged`; 1 for every other
8
+ * status; 2 when the pull request cannot be named; 3 when the read could not
9
+ * be answered (no credential, evidence off for the organization, unknown
10
+ * pull request, refused credential, unreachable host) — an unanswered read is
11
+ * never "not converged".
12
+ */
13
+ export declare const EVIDENCE_EXIT: {
14
+ readonly converged: 0;
15
+ readonly ok: 0;
16
+ readonly notConverged: 1;
17
+ readonly usage: 2;
18
+ readonly unanswered: 3;
19
+ };
20
+ export interface EvidenceDeps {
21
+ rclVersion: string;
22
+ fetchImpl?: typeof fetch;
23
+ env?: Record<string, string | undefined>;
24
+ cwd?: string;
25
+ credentialsPath?: string;
26
+ /** The repository the checkout's `origin` names; injected by tests. */
27
+ remoteRepo?: () => Promise<RepoRef | null>;
28
+ stdout: (line: string) => void;
29
+ stderr: (line: string) => void;
30
+ }
31
+ export interface StatusOptions {
32
+ json?: boolean;
33
+ enforced?: boolean;
34
+ }
35
+ /** The read sink, or `null` after telling the user why there is none. */
36
+ export declare function openSink(deps: EvidenceDeps): Promise<HarnessSink | null>;
37
+ export declare function runEvidenceStatus(prArg: string | undefined, options: StatusOptions, deps: EvidenceDeps): Promise<number>;
@@ -0,0 +1,72 @@
1
+ import { openReadSink } from '../telemetry/read-sink.js';
2
+ import { describeOutcome } from '../telemetry/sink.js';
3
+ import { formatGateStatus, safeJson, text } from './format.js';
4
+ import { getGateStatus } from './reads.js';
5
+ import { needsRemote, parsePullRequestArg, resolveRemoteRepo } from './target.js';
6
+ /**
7
+ * `rcl evidence status [<pr>]` (RCL-41): the gate status Harness computed for
8
+ * a pull request — never the client's own claim. The exit code is the
9
+ * contract the skills gate on: 0 only when the judged projection (advisory by
10
+ * default, `--enforced` on request) is `converged`; 1 for every other
11
+ * status; 2 when the pull request cannot be named; 3 when the read could not
12
+ * be answered (no credential, evidence off for the organization, unknown
13
+ * pull request, refused credential, unreachable host) — an unanswered read is
14
+ * never "not converged".
15
+ */
16
+ export const EVIDENCE_EXIT = { converged: 0, ok: 0, notConverged: 1, usage: 2, unanswered: 3 };
17
+ /** The read sink, or `null` after telling the user why there is none. */
18
+ export async function openSink(deps) {
19
+ const opened = await openReadSink({
20
+ rclVersion: deps.rclVersion,
21
+ ...(deps.env !== undefined ? { env: deps.env } : {}),
22
+ ...(deps.cwd !== undefined ? { cwd: deps.cwd } : {}),
23
+ ...(deps.credentialsPath !== undefined ? { credentialsPath: deps.credentialsPath } : {}),
24
+ ...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}),
25
+ });
26
+ if (!opened.sink) {
27
+ deps.stderr(`Cannot read evidence: ${opened.note}`);
28
+ return null;
29
+ }
30
+ return opened.sink;
31
+ }
32
+ export async function runEvidenceStatus(prArg, options, deps) {
33
+ if (prArg === undefined || prArg.trim() === '') {
34
+ deps.stderr('Name the pull request: N or #N (against the current git remote), owner/repo#N, or its URL.');
35
+ return EVIDENCE_EXIT.usage;
36
+ }
37
+ let target;
38
+ try {
39
+ const remote = needsRemote(prArg)
40
+ ? await (deps.remoteRepo ?? (() => resolveRemoteRepo(deps.cwd ?? process.cwd())))()
41
+ : null;
42
+ target = parsePullRequestArg(prArg, remote);
43
+ }
44
+ catch (err) {
45
+ // The argument or the remote may be the reason; neither reaches the terminal raw.
46
+ deps.stderr(text(err instanceof Error ? err.message : String(err), 400));
47
+ return EVIDENCE_EXIT.usage;
48
+ }
49
+ const sink = await openSink(deps);
50
+ if (!sink)
51
+ return EVIDENCE_EXIT.unanswered;
52
+ const outcome = await getGateStatus(sink, target.owner, target.repo, target.number);
53
+ const name = text(`${target.owner}/${target.repo}#${target.number}`, 200);
54
+ if (outcome.kind !== 'ok') {
55
+ deps.stderr(`Cannot read the gate status of ${name}: ${describeOutcome(outcome)}`);
56
+ return EVIDENCE_EXIT.unanswered;
57
+ }
58
+ const status = outcome.value;
59
+ const judged = options.enforced ? 'enforced' : 'advisory';
60
+ if (options.json) {
61
+ deps.stdout(safeJson(status));
62
+ }
63
+ else {
64
+ for (const line of formatGateStatus(status, judged))
65
+ deps.stdout(line);
66
+ }
67
+ // Converged is only ever conclusive on the server; a payload saying
68
+ // otherwise does not open the gate.
69
+ const projection = status[judged];
70
+ return projection.status === 'converged' && projection.conclusive ? EVIDENCE_EXIT.converged : EVIDENCE_EXIT.notConverged;
71
+ }
72
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/evidence/status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { parseRepoName, type RepoRef } from '../resolver/github.js';
2
+ export { parseRepoName, type RepoRef };
3
+ /**
4
+ * Which pull request `rcl evidence status` asks about: `owner/repo#N` or a
5
+ * pull request URL stand alone; a bare `N` or `#N` is read against the
6
+ * repository the current checkout's `origin` remote points at.
7
+ */
8
+ export interface PullRequestRef extends RepoRef {
9
+ number: number;
10
+ }
11
+ /** The GitHub repository a remote URL names, or `null` for anything else. */
12
+ export declare function parseRemoteUrl(url: string): RepoRef | null;
13
+ /** The repository `origin` points at, or `null` outside a checkout with a GitHub remote. Never throws. */
14
+ export declare function resolveRemoteRepo(cwd?: string): Promise<RepoRef | null>;
15
+ /** Whether an argument is a bare number or `#N`, which needs the remote to become a pull request. */
16
+ export declare function needsRemote(arg: string): boolean;
17
+ export declare function parsePullRequestArg(arg: string, remote: RepoRef | null): PullRequestRef;