Skip to content

Extract entire investigate into the entire-investigate plugin - #2515

Merged
Soph merged 7 commits into
mainfrom
soph/extract-investigate-plugin
Sep 21, 2026
Merged

Soph merged 7 commits into
mainfrom
soph/extract-investigate-plugin

Conversation

@Soph

@Soph Soph commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

https://entire.io/gh/entireio/cli/trails/1362

Summary

Moves entire investigate into entireio/entire-investigate, removing ~13k lines of built-in command code. Existing invocations and flags work unchanged once the plugin is installed.

  • Offers on-demand installation for investigate, alongside graph, and refreshes a stale plugin index once before reporting a missing entry.
  • Preserves session, provenance, and checkpoint metadata contracts for existing hooks and stored checkpoints.
  • Ignores legacy investigate settings without breaking settings loading. Configuration and prompt-trust enforcement now belong to the plugin; existing trust-gate tests are retained using review prompts.

Installation and migration

Run entire plugin install investigate (v0.1.0 is published), or accept the install prompt from entire investigate.

To retain existing configuration, copy the old investigate object into .entire/investigate.local.json; otherwise regenerate it with entire investigate --edit. Legacy settings blocks are safely ignored.

Verification

  • mise run check passes (format, lint, unit, integration, and canary).
  • GOOS=windows go vet ./... passes; CI is green.
  • Verified external dispatch and installation from the published release.

Moves the multi-agent investigation command out of this repository and into
github.com/entireio/entire-investigate, where it ships as an external
command. `entire investigate` keeps working: the dispatcher resolves
`entire-investigate` on $PATH once the plugin is installed
(`entire plugin install investigate`).

Removed: cmd/entire/cli/investigate/ (and its flowchart subpackage), the
investigate bridge, cmd/entire/cli/agentlaunch/ (investigate was its only
consumer), the integration test, the labs and agent-help entries, and the
root registration. 13k lines.

Kept, deliberately: everything investigation-related that the CLI reads on
its own behalf. session.KindAgentInvestigate and the run-id/topic fields,
provenance's ENTIRE_INVESTIGATE_* names, and the checkpoint metadata's
HasInvestigation/InvestigateRunID/InvestigateTopic are the contract the
plugin writes and this CLI consumes — condensation sets the umbrella flag
from the session kind, the lifecycle hook adopts the env vars, and `entire
status` and `entire explain` report both. None of that moves with the
command.

settings.InvestigateConfig becomes an unread json.RawMessage
-----------------------------------------------------------
The plugin now owns its configuration in .entire/investigate.local.json.
The `investigate` key here cannot simply be deleted: settings are decoded
with DisallowUnknownFields, so a leftover block makes the settings load
fail, and that disables the whole CLI in the repository rather than just the
moved command. Every repo that ran the built-in command has one, in the
untracked local file the picker wrote.

The field is therefore retained as a raw message that nothing reads — the
same treatment LocalDev already gets — with regression tests covering both
halves: a leftover key still loads and does not disturb the rest of the
file, and it no longer reaches the agent-instruction trust gate.

That gate loses its investigate arm. It can only gate fields the CLI
understands, and the CLI no longer feeds this one to anything; the plugin
applies the equivalent check to its own file, which has no committed
counterpart at all. The provenance tests that used investigate.always_prompt
as their vehicle are converted to a review worker prompt rather than
deleted — they cover the gate's layering and verification logic, not the
moved command.

docs/architecture/external-commands.md gains a section stating that settings
are not a plugin extension point, since this is the first extraction to hit
it and the next author will need the same answer.

Drive-by: `go mod tidy` demotes go-runewidth to indirect (investigate's TUI
was its only direct consumer), and filesystem-safety.md's "eighteen files
call settings.Load directly" was already stale before this change — it is 22
now, so the claim is reworded to stop tracking an exact count.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 01M2QRA3AZADW2PVC6Y71P0KEG
Copilot AI lite review requested due to automatic review settings September 17, 2026 13:29
@Soph
Soph requested a review from a team as a code owner September 17, 2026 13:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical and moderate findings remain.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Extracts entire investigate into an external plugin while preserving lifecycle metadata, checkpoints, and legacy settings compatibility.

Changes:

  • Removes the built-in investigation implementation and registration.
  • Retains legacy settings as unread raw JSON.
  • Updates trust handling, documentation, and dependencies.

Review notes:

  • Moderate (1 vote): Complexity tooling still references the removed investigate command.
  • Nit (2 votes): A settings comment still describes the removed investigate trust gate.
  • Moderate (1 vote): Plugin help may display entire-investigate instead of the compatible entire investigate path.
  • Moderate (2 votes): Generated plugin configuration is not automatically ignored by Git.
  • Critical (1 vote): Plugin .entire path handling does not validate against symlink redirection.
File summaries
File Summary
README.md Removes the built-in investigate command listing.
go.mod Demotes go-runewidth to an indirect dependency.
docs/security-and-privacy.md Documents plugin prompt-trust behavior.
docs/development/filesystem-safety.md Updates filesystem-safety references.
docs/development/cli-conventions.md Removes built-in investigate references.
docs/development/checkpoint-implementation.md Updates checkpoint documentation.
docs/architecture/external-commands.md Documents plugin settings constraints.
cmd/entire/cli/settings/settings.go Preserves legacy investigate settings as raw JSON.
cmd/entire/cli/settings/settings_test.go Adds legacy-settings compatibility coverage.
cmd/entire/cli/settings/agent_prompt_trust.go Removes CLI-owned investigate prompt gating.
cmd/entire/cli/settings/agent_prompt_trust_test.go Retargets trust tests to review prompts.
cmd/entire/cli/root.go Removes built-in command registration.
cmd/entire/cli/lifecycle_test.go Uses retained provenance constants.
cmd/entire/cli/labs.go Removes the investigate labs entry.
cmd/entire/cli/investigate/tui_text.go Removed and moved to the external plugin.
cmd/entire/cli/investigate/tui_sink.go Removed and moved to the external plugin.
cmd/entire/cli/investigate/tui_sink_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/tui_model_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/tui_detail.go Removed and moved to the external plugin.
cmd/entire/cli/investigate/tui_detail_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/testdata/prompt-with-always.txt Removed with the moved prompt tests.
cmd/entire/cli/investigate/testdata/prompt-mid-loop.txt Removed with the moved prompt tests.
cmd/entire/cli/investigate/testdata/prompt-first-round.txt Removed with the moved prompt tests.
cmd/entire/cli/investigate/state_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/show.go Removed and moved to the external plugin.
cmd/entire/cli/investigate/show_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/prompt.go Removed and moved to the external plugin.
cmd/entire/cli/investigate/prompt_yn.go Removed and moved to the external plugin.
cmd/entire/cli/investigate/prompt_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/progress.go Removed and moved to the external plugin.
cmd/entire/cli/investigate/progress_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/picker.go Removed and moved to the external plugin.
cmd/entire/cli/investigate/picker_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/notify_internal_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/multipicker.go Removed and moved to the external plugin.
cmd/entire/cli/investigate/multipicker_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/manifest_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/fix.go Removed and moved to the external plugin.
cmd/entire/cli/investigate/fix_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/findings.go Removed and moved to the external plugin.
cmd/entire/cli/investigate/findings_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/env.go Removed and moved to the external plugin.
cmd/entire/cli/investigate/env_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/cmd_internal_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/clean.go Removed and moved to the external plugin.
cmd/entire/cli/investigate/clean_test.go Removed with the moved implementation.
cmd/entire/cli/investigate/bootstrap.go Removed and moved to the external plugin.
cmd/entire/cli/investigate/bootstrap_test.go Removed with the moved implementation.
cmd/entire/cli/investigate_bridge.go Removes the investigate bridge.
cmd/entire/cli/investigate_bridge_test.go Removes bridge tests.
cmd/entire/cli/experimental_wiring_test.go Removes investigate wiring coverage.
cmd/entire/cli/agentlaunch/launch.go Removes the investigate-only launch helper.
cmd/entire/cli/agentlaunch/launch_test.go Removes launch-helper tests.
cmd/entire/cli/agent_help_cmd.go Removes investigate agent-help metadata.
cmd/entire/cli/agent_help_cmd_test.go Updates agent-help expectations.
Review details

Suppressed comments (2)

cmd/entire/cli/investigate/cmd.go:1

  • Removing this package leaves the current complexity renderer broken: tools/complexity/render.py still calls command("investigate.NewCommand") and includes INV in EXP_TOTAL, but the regenerated CLI reach graph can no longer contain that root, so report generation raises KeyError. Please remove or replace that complexity metric and update the feature paths as part of the extraction.
    docs/architecture/external-commands.md:243
  • The plugin's root command overrides Cobra's original Use: "investigate [seed-doc]" with "entire-investigate [seed-doc]", so generated help/usage now identifies entire-investigate rather than the existing entire investigate command. That contradicts the PR's byte-identical compatibility claim; preserve the old displayed command path (or add an explicit compatibility exception and help snapshot).
  • Files reviewed: 67/67 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/security-and-privacy.md
Comment thread docs/security-and-privacy.md
Comment thread cmd/entire/cli/settings/settings.go
Soph and others added 3 commits September 17, 2026 15:48
…ate-plugin

# Conflicts:
#	docs/development/cli-conventions.md
provenance's package comment and IsEntry both cited agentlaunch as the
caller that strips provenance markers before spawning a fix agent. That
package moved to the entire-investigate plugin, so the names now describe a
contract that crosses a process boundary: review is in this repository, the
plugin imports provenance for the same constants. IsEntry itself is
unchanged and still has an in-repo caller in review/env.go.

gitdir's OpenPathIn comment pointed at investigate.StateStore as the example
of the stronger anchoring form. The store still exists and still reaches
gitdir through OpenAt; only its address changed.

Found by Entire Gates on this PR (01M2QS2RCT9Y).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 01M2QTQW5AK7JGC6MNKX8XTBDN
loadForWorktreeRoot listed investigate.always_prompt among the fields
enforceAgentPromptTrust gates. The investigate arm was removed with the
command, and this PR's own regression test asserts the key is no longer
gated, so the comment contradicted the code it sits above.

Found by review on this PR.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 01M2QZTAFF4GW6D1C57YGVW8X1
pjbgf
pjbgf previously approved these changes Sep 17, 2026
`entire investigate` was a built-in until this PR moved it to a plugin, so a
user who types it has every reason to expect the command to exist. Without
the offer it now answers an established command with Cobra's "unknown
command for entire" — accurate, and no help at all.

`graph` already had this treatment, as the one name the dispatcher offered
to install rather than falling through. onDemandInstallPluginName becomes
onDemandInstallPluginNames, with offersOnDemandInstall as the single
predicate the resolver asks.

The set stays narrow on purpose, and membership is not a reward for being
official: `run` and `upgrade` are both index-listed and both stay out. The
offer is a prompt defaulting to Yes that ends in a downloaded binary linked
onto $PATH, so it belongs only to names this CLI previously answered itself.
A regression test pins both directions, because the cost of a wrong addition
is not a failed lookup.

One prerequisite is worth stating where the next person will look: a name in
the set must be resolvable through the plugin index, since the install path
looks it up there and nowhere else. `investigate` is not indexed yet, so
until it is, this converts one unhelpful message into another — the prompt
resolves, finds nothing, and says so. It becomes useful the moment the index
entry lands, and the alternative was shipping the plugin with the CLI having
no answer for its own former command.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 01M2R110H1XV8PZKSZG13XX5VR
Soph and others added 2 commits September 17, 2026 18:30
The catalog clone refreshes on a 24-hour TTL, so a plugin added today stays
invisible to a cached copy for up to a day. The on-demand path then reports
that the plugin "is not listed in the plugin index" — blaming the catalog
for being wrong when the local copy is merely old, and pointing the user at
the one thing that is already correct.

That is not a corner case. It is the window immediately after a plugin is
published, which is exactly when someone tries to install it. Hit for real
with entire-investigate: the index entry had already merged, and
`entire investigate` still reported it unlisted.

A miss now forces one refresh and re-checks before reporting. The cost is
confined: it runs only on a path that was about to fail and exit non-zero,
only for the names in onDemandInstallPluginNames, and never on the hit path.
Offline it cannot make things worse either — SyncPluginIndex falls back to
the stale copy with a logged warning instead of failing, so the worst case
is the same miss and the same message.

The message says "refreshed for this check" rather than claiming the data is
current, because that fallback means a refresh can be attempted without
producing fresher data.

The regression test primes the cache with a catalog that predates the entry,
adds the entry upstream, and asserts the install resolves to the newly
indexed repository. It fails with the forced sync removed, reproducing the
reported error verbatim.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 01M2R397V7J2TKYJ85CTNQWQQW
A reviewer reading `Investigate json.RawMessage` reasonably asks whether
unvalidated bytes are being carried through the merge. They are not, and the
reason was not written down.

A json.RawMessage is only ever populated by a decoder that scanned the value
to find its end, so malformed content fails the surrounding parse before it
reaches the field. mergeJSON puts two gates in front of it: a strict
EntireSettings decode, then the map decode it takes the raw block from.
Validating the bytes again in mergeInvestigate could never fail.

What the field does promise is the other half: any well-formed value is
accepted and none of it is interpreted. The shape of an old investigate
block stopped being this CLI's business when the command left, and checking
it would re-couple the two.

Tests pin both halves — arbitrary content kept verbatim, a scalar accepted
as readily as an object, and malformed or truncated input rejected by the
parse rather than by a field-level check.

Raised as finding 01M2R3DWP2K8 on this PR's trail, which read the missing
validation as a silent-acceptance risk.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 01M2RGCE2TR2K16R0YZ80VS8KR
@Soph
Soph merged commit 42080fa into main Sep 21, 2026
17 checks passed
@Soph
Soph deleted the soph/extract-investigate-plugin branch September 21, 2026 09:48
Soph added a commit that referenced this pull request Sep 21, 2026
#2515 landed on main and removed the investigate entry from labs.go, which
this branch had edited to use a constant — the one conflict, resolved by
taking main's deletion.

That merge also moved the goconst pressure rather than removing it. The
three occurrences of the `investigate` literal are now lifecycle.go's
kindLabel, plugin.go's onDemandInstallPluginNames (which arrived with
#2515), and the allowlist entry this branch adds. So the constants this
branch introduced before the merge are the wrong ones: cmdInvestigate and
sessionKindLabelInvestigate are both reverted, and a single
investigatePluginName in plugin.go — beside selfUpdatePluginName, whose
single use makes that the established shape there — takes the dispatcher's
occurrence out of the count.

That leaves two literals, below the threshold, and keeps officialPlugins a
uniform table of plain strings rather than one symbol among four literals.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 01M31QY4ZDYWFPBD8R0YH9CDRS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants