{
  "v": 0,
  "_what_this_is": "The library's second entry: the grant of a GitHub Actions runner, generated by running tools/measure.py INSIDE the runner rather than written from documentation. It is the environment an agent's push reaches into — entry #1 ends at node n3 (a push), and this is what happens next — so the two entries join at that edge and together they are the blast-radius path rather than two unrelated points.",
  "environment": {
    "product": "github-actions-runner",
    "surface": "ephemeral CI runner, ubuntu-latest, workflow declaring contents:read",
    "vendor_named": true,
    "note": "one vendor, one runner image, one date. Named here because the control being measured — the workflow permissions block — is a documented public feature rather than a security grade"
  },
  "measured_at": "2026-08-26",
  "measured_by": {
    "who": "tools/measure.py, executing inside the runner it measures — the instrument IS the subject",
    "tool": "packs/grant-and-mandate/tools/measure.py",
    "provenance": "GitHub Actions run 32984977257, job 98229605755, workflow measure-grant, commit be835d4, 2026-08-26T15:25:44Z. The job log is the evidence; the entry below is that log's JSON, transcribed with the interpretation block added.",
    "caveat_floor_not_census": "an agent measuring its own grant reports what it can see; a capability it does not know it has will not appear here"
  },
  "history": {
    "retained": false,
    "window": "none — the runner is provisioned per job and destroyed after it. Nothing carries forward except what is deliberately uploaded as an artefact or committed",
    "why_this_field_exists": "with history retained the grant is a union over prior sessions; here it genuinely is not, which is the cleanest contrast the library holds against entry #1"
  },
  "measurement_honesty": {
    "probes_refused_or_unknown": 2,
    "detail": [
      "n5: no agent-harness settings file at the usual paths; whether one exists above the job was not established",
      "n6: no retained session record found — consistent with an ephemeral runner, but recorded as unknown rather than asserted as absent"
    ],
    "rule": "a refused probe is recorded as unknown, never guessed"
  },
  "nodes": [
    {
      "id": "n1", "parent": null,
      "capability": "runs as uid 1001",
      "reachable": "every file and process this user can reach; bounded by this user's permissions",
      "tier": "boundary",
      "control": "the OS user separation",
      "evidence": "observed",
      "method": "geteuid()=1001; platform=Linux",
      "SUPERSEDED_BY": "see interpretation.finding_1 — this tier label is WRONG, and node n1a is the proof"
    },
    {
      "id": "n1a", "parent": "n1",
      "capability": "escalate to administrator",
      "reachable": "everything, without a further credential",
      "tier": "none",
      "control": null,
      "evidence": "observed",
      "method": "`sudo -n true` -> succeeded"
    },
    {
      "id": "n2", "parent": "n1",
      "capability": "read and write this user's home directory",
      "reachable": "every file this user owns; no credential-shaped paths found at the usual locations",
      "tier": "none",
      "control": null,
      "evidence": "observed",
      "method": "existence check at known paths under $HOME — presence only, no file was opened or read"
    },
    {
      "id": "n3", "parent": "n1",
      "capability": "outbound network access",
      "reachable": "hosts reachable: github.com 200, pypi.org 200, example.com 200 — UNRESTRICTED",
      "tier": "none",
      "control": null,
      "evidence": "observed",
      "method": "curl to 3 hosts; HTTPS_PROXY not set"
    },
    {
      "id": "n4", "parent": "n3",
      "capability": "push commits to the configured remote",
      "reachable": "the repository at github.com/SGit-AI/SGit-AI__Website__PKI",
      "tier": "expectation",
      "control": "branch discipline, if any, lives in prose",
      "evidence": "observed",
      "method": "git remote present; core.hooksPath=unset — see interpretation.finding_2"
    },
    {
      "id": "n5", "parent": "n1",
      "capability": "agent harness configuration",
      "reachable": "UNKNOWN — not established",
      "tier": "unknown",
      "control": null,
      "evidence": "none",
      "method": "no settings file found at the usual paths; whether one exists above this job was not established"
    },
    {
      "id": "c1", "parent": null,
      "capability": "runs as an ephemeral CI job",
      "reachable": "workflow 'measure-grant' on SGit-AI/SGit-AI__Website__PKI; runner Linux x86_64. The machine is destroyed after the job, so nothing persists between runs except what is deliberately uploaded",
      "tier": "boundary",
      "control": "the runner is provisioned per job and discarded; no state carries forward without an explicit artefact or commit",
      "evidence": "observed",
      "method": "GITHUB_ACTIONS environment, read at run time"
    },
    {
      "id": "c2", "parent": "c1",
      "capability": "act on the repository with the job's token",
      "reachable": "bounded by the workflow's `permissions:` block — this job declares contents:read and nothing else, so the token cannot write to the repository, publish, or dispatch another workflow",
      "tier": "boundary",
      "control": "the `permissions:` block, set in the workflow file above the job; the job cannot widen its own token at run time",
      "evidence": "observed",
      "method": "token present in environment: False (this job did not pass one to the step); scope declared in .github/workflows/measure-grant.yml"
    },
    {
      "id": "c3", "parent": "c1",
      "capability": "read the repository contents",
      "reachable": "every file in the checked-out tree at this ref — including anything a contributor committed by mistake",
      "tier": "none",
      "control": null,
      "evidence": "observed",
      "method": "actions/checkout ran before this step"
    },
    {
      "id": "n6", "parent": "n1",
      "capability": "read this environment's accumulated session record",
      "reachable": "no retained session record found at the usual paths",
      "tier": "unknown",
      "control": null,
      "evidence": "none",
      "method": "existence check only — no transcript was opened"
    }
  ],
  "interpretation": {
    "_note": "Added after the measurement, by reading it. The nodes above are the tool's raw output, transcribed unedited so the two findings below are checkable against it.",
    "finding_1": {
      "title": "A boundary defeated by the very next node — the pack's own warning, caught in its own output",
      "detail": "n1 labels the OS user separation a `boundary`. n1a records that `sudo -n true` SUCCEEDS: passwordless escalation to root, no further credential. So the separation bounds nothing — it is defeated by a capability the same grant includes, which is precisely the pack's definition of a SETTING rather than a boundary. n1's tier is wrong.",
      "why_it_matters": "The tool mislabelled it because it evaluated each node in isolation. That is the exact failure mode document 12 of the registry pack warns about — 'most of what people currently rely on is a setting that reads like a boundary' — reproduced by an automated measurer rather than by a vendor's marketing. A tier is a property of a node's RELATIONSHIP to the rest of the tree, not of the node.",
      "correction": "n1 should read tier `setting` (or `none`), with the control annotated 'defeated by n1a'. The tool has been corrected to downgrade a claimed boundary when a child node shows free escalation; this entry keeps the original label with SUPERSEDED_BY so the defect stays visible."
    },
    "finding_2": {
      "title": "The pre-push hook does not travel with a clone",
      "detail": "n4 reports `core.hooksPath=unset` and therefore tier `expectation` — in a repository whose tree DOES contain .githooks/pre-push. The hook file is committed; the git config that activates it is local and is not part of the tree.",
      "why_it_matters": "Entry #1 records n4 as `setting` because that session had run `git config core.hooksPath .githooks`. Any fresh clone — a CI runner, a new contributor, the same agent in a new container — gets the file and not the enforcement. The control is one un-run command away from being absent, and nothing announces that.",
      "consequence": "This is a real limitation of build-order step 1 as built, found by measurement rather than by review, and it strengthens the case for decision GM-D26: a boundary is the same allow-list evaluated where the agent cannot reach it (branch protection, a required CI check), which needs no per-clone setup step at all."
    },
    "contrast_with_entry_1": {
      "identity": "entry #1 runs as root with no internal separation; this runs as uid 1001 — and n1a shows the separation is free to escalate past, so the practical difference is smaller than the labels suggest",
      "egress": "entry #1 is behind a MANDATORY proxy and a non-allowlisted host was connection-reset; this runner reached example.com with a 200 — UNRESTRICTED. The hosted agent is more contained on the network than the CI runner that deploys its work",
      "history": "entry #1 retains a session record (the grant is a union over prior turns); this retains nothing (the grant is a tree over the present). The clearest illustration in the library of why the `history` field changes the meaning of every node",
      "token": "this runner's grant is DECLARED UP FRONT in the workflow's permissions block, set above the job by something the job cannot edit — the only genuine boundary in either entry that was designed as one rather than discovered"
    }
  },
  "licence": "CC BY 4.0"
}
