diff --git a/README.md b/README.md
index 099abcb..b9a1f78 100644
--- a/README.md
+++ b/README.md
@@ -46,7 +46,7 @@ Get your API token at [app.garnet.ai](https://app.garnet.ai). Start with the Act
/>
-The action's own comment shape (renderer v6.9.5); the companion GitHub App comment adds true coverage and cross-run comparison.
+The action's own comment shape. The renderer emits contract v6.10.0; this capture is from v6.9.5, before the meta block split into a finding line and a quiet provenance line. The companion GitHub App comment adds true coverage and cross-run comparison.
## What Garnet sees
@@ -145,7 +145,7 @@ Full installation guides for every path are in the [Garnet docs](https://docs.ga
One comment per PR, one fold per job, updated in place as each job's profile lands:
- **Headline** — `Execution Profiles recorded for N job(s), triggered by `, linking the commit.
-- **Metadata line** — an italic blockquote: `N destinations · recorded at the kernel by Garnet · `, one fact per `·` segment.
+- **Meta block** — two blockquote lines. The italic finding line first: `N destinations` on a first record, the job segments and `compared with ` on a comparison, `No changes since ` when nothing moved. Then one quiet line: `recorded at the kernel by Garnet · `, at minute precision. One fact per `·` segment.
- **One fold per job** — headed `workflow / job ↗ · N destinations`, the job id linking to its Actions run. Inside: one block holding every recorded root of the job's tree; independent roots are separated by a blank line. Plain tree nodes are recorded process names; observed actions render as shaped terminals — `○ destination` for network, defanged at the final dot. A process with an action directly beneath it renders **bold**; `(…)` brackets carry factual context only — `(step: "Run tests")`, `(dns resolver)`, `(cloud metadata)`, `(github infra)`, `(garnet sensor)`, `(ran from /tmp/…)`. A job with no recorded egress stays a plain row keeping its Garnet profile link.
- **Per-job permalink** — `View this job's Execution Profile in Garnet →`, opening the job's [public run report](https://app.garnet.ai/public/runs/31257440827?profile=019fe15d-b34f-7803-820a-ecf58404a278) (`?profile=` selector required — a bare run URL returns 404).
- **The explainer** — a `💡 How to read this` fold at the bottom teaches the tree with an annotated example:
@@ -163,6 +163,8 @@ names on the path = processes · ○ = observed action · (…) = context
Once a pull request has two recorded commits, the comment compares against the previous profiled commit: the metadata line carries `compared with `, changed job rows lead with the bold delta (`+1 −2 destinations`), unchanged rows read `· N destinations · unchanged`, and a changed job's tree renders as a diff headed `@@ (previous) vs (current) @@`. `+` marks a destination only in the current record, `−` one only in the previous record; the marks and the row's delta always reconcile exactly. Jobs recorded previously but not on this commit sit in one collapsed `jobs no longer recorded` fold with their destination counts.
+The deltas on the meta line and on the fold rows count your workflow's destinations. Movement in the runner's own background is counted where it happened: the root line above those marks carries `(runner background · +A −B)`, so every mark stays visible and every number counts what sits beneath it.
+
The same full-detail record is appended to the GitHub Actions Job Summary as the **Garnet Execution Summary** (see this [example run](https://github.com/garnet-org/action/actions/runs/23175135499)).
## Under the hood
@@ -183,6 +185,12 @@ The same full-detail record is appended to the GitHub Actions Job Summary as the
| `debug` | No | `false` | Enable debug mode and upload logs as artifacts |
| `preview` | No | `false` | Render the full-fidelity Step Summary record (assertions + evidence); preview shape is unstable and may change without a major version bump |
+### Environment variables
+
+| Variable | Default | Description |
+| ------------------------------------- | ------- | -------------------------------------------------------------------------------------------------- |
+| `GARNET_JIBRIL_STOP_TIMEOUT_SECONDS` | `1800` | Seconds the Jibril systemd unit gets to flush its events on stop. The main step writes it as a `TimeoutStopSec` drop-in; the post step reads the unit's effective value and bounds its own wait to it. Raise it for long jobs whose profile goes missing after a stop. |
+
¹ Required at runtime, with one exception: on `pull_request` runs from forked repositories, GitHub exposes no secrets, so the action emits a notice, skips recording, and the job continues. An OIDC alternative is landing behind the `GARNET_ACTION_ENABLE_OIDC_AUTH` flag; until that flag is on, the token is the auth path.
---
diff --git a/docs/runtime-review-v6.2.md b/docs/runtime-review-v6.2.md
index 44bce29..8524a42 100644
--- a/docs/runtime-review-v6.2.md
+++ b/docs/runtime-review-v6.2.md
@@ -1,8 +1,37 @@
-# Runtime Review v6.2 — surfaces, framing, and the preview flag
+# Runtime Review v6.2 — historical record of the v6.1 → v6.2 step
-Contract source of truth: `garnet-org/runtime-review-testbed` `docs/ux-contract.md`
-(v6.2). The testbed's byte-gated goldens are the spec; this action's renderer
-(`src/runtime-review.js`) is a port of the testbed reference renderer.
+Status: **superseded.** The contract has moved to v6.10.0. This page is kept
+for the reasoning behind the v6.2 step, not as a description of what the
+action renders today.
+
+Contract source of truth: `garnet-org/runtime-review-testbed`
+`contract/vocab.json` **6.10.0** + `docs/ux-contract.md`. The testbed's
+byte-gated goldens are the spec. This action's renderer
+(`src/runtime-review.js`) is a port of the testbed reference renderer and
+vendors the contract byte-identically as `src/runtime-review-vocab.js`; read
+that file for the strings the renderer actually emits.
+
+## What v6.10.0 says instead
+
+Everything in the "What changed in the PR comment" section below is
+superseded. The current shape:
+
+- Headline: `**Execution Profiles recorded for N job(s), triggered by
+ **` — bold body text, no heading, no product name.
+- Meta block: two blockquote lines. The italic finding line first (the
+ destination total, or the job segments and `compared with `, or
+ `No changes since `), then one quiet line
+ `recorded at the kernel by Garnet · ` at minute precision. The
+ words `as of` no longer render.
+- One fold per job, one block per fold, holding every recorded root of that
+ job. Fold rows carry `· N destination(s)`; changed rows lead with a bold
+ `+A −R` split that counts your workflow's destinations. Movement in the
+ runner's background is counted by a `(runner background · +A −B)` label on
+ the root above those marks.
+- Observed actions render as `○ ` leaves. The explainer teaches
+ one execution chain: a path from a root down to an observed action.
+- Fold summaries carry counts and identity only — the `reached` sentence is
+ gone.
## Two surfaces, one renderer
@@ -13,7 +42,7 @@ Contract source of truth: `garnet-org/runtime-review-testbed` `docs/ux-contract.
It is a faithful, readable projection of the Run Profile artifact: could you
reconstruct the profile's story from it? That is the test it must pass.
-## What changed in the PR comment (v6.1 → v6.2)
+## What changed in the PR comment (v6.1 → v6.2) — superseded, see above
- Marker block: canonical marker, self marker, then ``.
- Actor-conditional heading: standalone mode (this action, `github-actions[bot]`)
@@ -32,7 +61,7 @@ Contract source of truth: `garnet-org/runtime-review-testbed` `docs/ux-contract.
absent in the waiting state.
- Job fold summaries use the verb **reached** (never `contacted`).
-## Step Summary: delta vs Djalal's markdown printer (jibril)
+## Step Summary: delta vs Djalal's markdown printer (jibril) — v6.2 shape
Djalal's most recent Go markdown printer (`pkg/printers/profiler/markdown.go`,
"Garnet - Runtime Report") was destination-first and full-fidelity: Profile
@@ -41,8 +70,10 @@ telemetry — but it also carried verdict framing (✅/❌ headline, per-row sta
icons, bad-first sorting) and a lossy remote-name dedupe ("omitted
destinations").
-The v6.x Step Summary restores Djalal's faithful-record shape while removing
-only the verdict framing:
+The v6.2 Step Summary restored Djalal's faithful-record shape while removing
+only the verdict framing. The v6.10.0 Step Summary keeps that intent, but the
+table is lineage-first (`Process Tree | Destinations`), not the
+destination-first table described here:
- Egress table is **destination-first** (`Destination | Port | Lineage Tree`),
one row per recorded destination in the profile's own
@@ -77,6 +108,6 @@ with their own fixtures so prod bytes cannot drift while preview evolves.
The action renders from the JSON Run Profile, so jibril's own Go markdown
printer output ("Garnet - Runtime Report", ✅/❌ headline, `contacted`) is no
longer user-visible through this action — no runtime coupling breaks. If that
-printer's output resurfaces on a user-facing path, it should adopt the v6.2
-`VOCAB` strings. Ashkaal is the schema layer (field names) and is untouched by
+printer's output resurfaces on a user-facing path, it should adopt the
+contract strings vendored in `src/runtime-review-vocab.js`. Ashkaal is the schema layer (field names) and is untouched by
renderer vocabulary.
diff --git a/docs/tokenless-path-spec.md b/docs/tokenless-path-spec.md
index f6046d6..f3c6729 100644
--- a/docs/tokenless-path-spec.md
+++ b/docs/tokenless-path-spec.md
@@ -1,30 +1,47 @@
# Tokenless path for the Garnet Action — design spec (draft)
-Status: **draft for review** — no product code changes in this PR.
+Status: **draft, not implemented.** Read it as a design proposal, not as
+behavior. Two things have moved under it since it was written:
+
+- The contract of record is now `garnet-org/runtime-review-testbed`
+ `contract/vocab.json` **6.10.0** + `docs/ux-contract.md`, vendored in this
+ repo as `src/runtime-review-vocab.js`. Every v6.1/v6.2 string and section
+ reference below is stale. The v6.2 amendment this spec asks for was never
+ ratified: 6.10.0 contains no tokenless, local-only, or upgrade-CTA
+ vocabulary. The permalink it calls
+ `View Run Profile in Garnet ↗` is today
+ `View this job's Execution Profile in Garnet →`, and the record is an
+ Execution Profile, not a Run Profile.
+- `api_token` is already `required: false` in `action.yaml` (§3 landed), but
+ the local-only branch did not: `src/action.js` still throws
+ "Input 'api_token' is required…" when neither a token nor OIDC produces
+ control-plane auth. The fork-PR path taken since is OIDC behind
+ `GARNET_ACTION_ENABLE_OIDC_AUTH`, not the tokenless mode described here.
Tracking: [ENG-1329](https://linear.app/garnet-labs/issue/ENG-1329/tokenless-path-for-github-action)
(tokenless path) and the fallback half of
[ENG-1346](https://linear.app/garnet-labs/issue/ENG-1346) (simpler comment
shape + token-optional local-only fallback). Sequenced against
[ENG-1355](https://linear.app/garnet-labs/issue/ENG-1355) (v6.x renderer port
-to this repo; ENG-1345 was canceled into it). Contract of record:
-`garnet-org/runtime-review-testbed` `docs/ux-contract.md` **v6.1** (testbed
-PRs #52/#53/#55/#59). Prior analysis: Devin session
+to this repo; ENG-1345 was canceled into it). Contract of record when this
+spec was written: `garnet-org/runtime-review-testbed` `docs/ux-contract.md`
+**v6.1** (testbed PRs #52/#53/#55/#59). Prior analysis: Devin session
`022557ef6b7d4534b9c5085147f23d67` (`token-optional-and-plg-review.md`) and
testbed `docs/step-summary.md` §"Tokenless-by-default fallback".
## 1. Motivation (PLG: value before signup)
-Today `api_token` is `required: true` and the main step throws when it is
-empty (`src/action.js` — "Input 'api_token' is required…"). The two biggest
+When this spec was written `api_token` was `required: true` and the main step
+threw when it was empty (`src/action.js` — "Input 'api_token' is required…").
+The input is optional now; the throw is still there. The two biggest
consequences:
- **Adoption friction**: a user must sign up, create a project, copy a token,
and add a repo secret *before first value*. The README one-liner cannot be
a one-liner.
- **Fork PRs always fail**: forks never receive repository secrets, so the
- action degrades to "no runtime monitoring" on exactly the PRs where an
- OSS maintainer most wants runtime evidence.
+ action records nothing on exactly the PRs where an OSS maintainer most
+ wants runtime evidence.
The goal: `uses: garnet-org/action@v2` with **no inputs** produces a complete
local Runtime Review — GitHub Step Summary always, standalone PR comment when