Skip to content

claude-ops/audit-install-state: label a remote cloud-session install tree instead of grading it as the operator's machine #4080

Description

@kyle-sexton

Context

A run of /claude-ops:audit-install-state on 2026-09-08 inside a Claude Code on the web container returned a confident "healthy, nothing stale" verdict on /root/.claude, a tree that was three minutes old and not the operator's workstation. The report carries no signal that the tree belongs to an ephemeral container, so a reader who invoked the skill from the web UI gets an answer about the wrong machine and has to notice the mtimes themselves.

Upstream documents one signal for this: CLAUDE_CODE_REMOTE is "Set automatically to true when Claude Code is running as a cloud session" (env-vars.md, fetched 2026-09-11). That variable describes the running process, not the scanned tree, and the engine accepts --root <path>, so it cannot be the tree verdict on its own. Tree-level signals observed in the container and absent from every official page fetched that day (claude-directory.md, plugins-reference.md, env-vars.md, hooks.md, cloud-environments.md, plugin-marketplaces.md, CHANGELOG through 2.1.268): launcher-settings.json, environment-manager/, and four hook scripts at the tree root. plugins/synced/ is documented (plugins-reference.md) and is also cloud-specific. session-env/, remote-settings.json, and policy-limits.json are NOT discriminating: the first is documented as per-session metadata on every install, the other two are server-managed settings caches that a local install can carry too.

The engine's evidence vocabulary is a closed set today (install_state.py header and reference/evidence-discipline.md section 1), so the new block needs an explicit vocabulary extension and a schema version bump rather than a silent parallel tag.

The cost of leaving it: every cloud-session run of a skill whose stated purpose is "is this tree in a deliberate or experimental state?" misreads the most common experimental state there is.

Proposed work

  • Add an environment block to the JSON report with two parts: a session-context line (CLAUDE_CODE_REMOTE value, tagged as a property of the auditing process, plus a root_is_own_config_dir boolean derived from --root versus the resolved config dir) and a tree-signal list (launcher-settings.json, environment-manager/, root-level *.sh / *.py hook scripts, plugins/synced/), each row carrying its own evidence tag.
  • Extend the evidence vocabulary explicitly (for example documented and observed-undocumented) in install_state.py and reference/evidence-discipline.md, and bump schema from claude-install-state/1 to /2; add tests for the new block, including the false-positive guard that a local tree with only session-env/ never reads as remote.
  • Add the tree-level verdict remote | local | indeterminate and require at least one tree signal for remote; a process-level CLAUDE_CODE_REMOTE=true with no tree signal, or conflicting signals, yields indeterminate.
  • SKILL.md: add the environment read as the first thing the reader checks, and reword the current "Before reading any other part of the report, read deliberate_state and deny_roots" sentence so the two instructions do not contradict each other; add one line pointing remote sessions at the session scratchpad for the --csv and JSON destinations.
  • While editing SKILL.md, move phase detail that the reference/ files already own out of the hub so the file drops under the skill-quality soft cap of 200 lines (it is 237 today and /skill-quality:check emits a WARN); the trim is part of this issue, not a separate one.
  • Record the undocumented signals with a four-part record (claim, basis, as-of date and CLI version, recheck trigger) per .claude/rules/skill-bodies-state-current-rules.md. Do not add CLAUDE_CODE_REMOTE_ENVIRONMENT_TYPE or other CLAUDE_CODE_REMOTE_* / CCR_* variables as signals until a page documents them.

Acceptance criteria

  • A run inside a cloud session (CLAUDE_CODE_REMOTE=true and launcher-settings.json present) emits environment.tree_verdict: remote with every signal row tagged, and the chat report's first line names the tree as the container's.
  • A run against a local tree that carries session-env/, remote-settings.json, and policy-limits.json but none of the tree signals emits tree_verdict: local.
  • IF CLAUDE_CODE_REMOTE=true but no tree signal is present, or signals conflict, THEN tree_verdict is indeterminate, never remote.
  • WHILE the run targets a remote tree, every staleness verdict in the report is reported as the container's, and no verdict changes value (label-only; the report-only contract is unchanged).
  • schema reads claude-install-state/2; the evidence vocabulary extension is documented in reference/evidence-discipline.md; the test suite covers the three verdicts.
  • SKILL.md no longer contains two "read this first" instructions that contradict each other.
  • /skill-quality:check audit-install-state reports no line-count WARN (SKILL.md under 200 lines) with no rule lost, only relocated to reference/.

References

  • Run evidence: 2026-09-08 report, tree age 3 minutes, quiesced: false, no deliberate_state, 13,734 files.
  • env-vars.md CLAUDE_CODE_REMOTE row; plugins-reference.md plugins/synced/; claude-directory.md session-env/ row (all raw markdown, fetched 2026-09-11).
  • plugins/claude-ops/skills/audit-install-state/scripts/install_state.py: --root handling, evidence constants, SURFACE_TABLE entry for session-env.
  • reference/evidence-discipline.md section 1 (evidence vocabulary), .claude/rules/skill-bodies-state-current-rules.md.
  • Related: the sibling issue on the largest-subtree attribution (same run), filed alongside this one.

Metadata

Field Value
Category unspecified
Area unspecified
Ecosystem unspecified

Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions