Skip to content

Managed entitlement: appMap.customerId - #959

Open
dividedmind wants to merge 12 commits into
mainfrom
feat/customer-id
Open

Managed entitlement: appMap.customerId#959
dividedmind wants to merge 12 commits into
mainfrom
feat/customer-id

Conversation

@dividedmind

Copy link
Copy Markdown
Contributor

Lets an administrator entitle a deployment through the configuration channels they already use, so developers can use the plugin without signing in to getappmap.com.

Where licensing is settled by an agreement with AppMap, an interactive sign-in adds nothing: it fails outright on restricted networks, and it creates an audit trail for a flow that grants nothing the agreement has not already granted. Setting appMap.customerId in the bundled site-config.json or in an organization configuration puts the plugin into its signed-in state.

This is not a licence key and not an enforcement mechanism. The value is unverified and the plugin is open source, so any client-side check is a business-process boundary, not a security one. It is also not a secret — it is shown in the settings UI and printed unredacted in the status report. Please keep both statements intact if you reword the docs.

Names match the VS Code plugin exactly: the config key appMap.customerId, the subprocess variable APPMAP_CUSTOMER_ID, and the telemetry property common.customerid (lower-case, no separator).

Port of getappmap/vscode-appland#1119

How to read this branch

Eleven commits, in three groups.

Three preliminary fixes — each stands on its own merits and can be reviewed independently.

Commit
fix(services): keep the plugin quiescent while it is inactive Longstanding bug: there was no authentication gate on anything. A signed-out user with an appmap.yml got an indexer, a JSON-RPC server and — if configuration enabled it — a scanner, all running behind a sign-in wall that presents the plugin as inactive. Also gates the Tools > AppMap actions.
fix(settings): resolve deployment defaults per field, and keep them fresh The settings panel offered "Use deployment settings" based on whether any deployment setting existed, so a telemetry-only configuration made both combo boxes claim a default they didn't have. Review then found a second bug: the combo values and comments were built once, so they went stale when an organization configuration was applied or cleared from the buttons in that same panel.
refactor(enterpriseConfig): derive settings changes from a captured EffectiveState Five sites hand-rolled a before/after pair per field to decide which change events to fire. Replaced with a record captured either side of the mutation.

Two supporting changes.

Commit
refactor(deployment): construct AppMapDeploymentSettings through a builder Every field is nullable and drawn from three types, so positional construction silently accepted a wrong ordering. The all-args constructor is now private.
test(enterpriseConfig): make the "event not fired" assertions able to fail Several negative assertions waited on an event published before the ones they asserted about, so they passed whether or not the event fired. Found by deliberately breaking the production code and watching a test stay green.

Six wip(entitlement|deployment) commits — the feature itself. Squash these before merge. They are split by concern to make review tractable, not because any of them stands alone:

  1. the customerId field, the merge, and the Entitlement accessor
  2. isSignedInOrEntitled() and the UI/service gate
  3. APPMAP_CUSTOMER_ID in the subprocess environment
  4. common.customerid on telemetry events
  5. the customerIdChanged() event, so entitlement applies without an IDE restart
  6. the status report, the settings display, and the docs

There is also a wip(docs) commit carrying the internal implementation plan. Drop it before merge — plan documents in this repo are deliberately untracked. It is here only to make review easier: it records the design reasoning, what was built versus what was originally specified, and a test matrix marking each line automated or manual.

Design notes worth a reviewer's attention

No new state, no seeding. The VS Code implementation carries a globalState projection, a provenance tag and a six-row seeding table, all of which exist to work around the fact that its settings API cannot distinguish "baked into this build" from "typed by the user". This plugin already resolves through three physically separate layers, so the whole resolution table falls out of the existing merge() — provided we add no user-settable override, which we deliberately don't.

Entitlement is a separate axis from credentials. getApiKey() still returns only real session tokens; nothing substitutes a customer ID for one. isSignedInOrEntitled() is for UI and service state; anything needing an actual token keeps using hasAppMapKey(). Both variables are passed to the CLI when both are present, and each is omitted rather than sent empty when absent, so the CLI can tell the states apart.

Blank collapses to absent, in one place — AppMapDeploymentSettings.getCustomerId(). Gson populates the field reflectively, so normalizing in a setter or the builder would miss the parsed configuration, which is the only production path. Keeping it in the accessor means merge() needs no special case and a blank organization value falls through to a bundled one instead of masking it.

Two deviations from the plan, both agreed in review and recorded there:

  • No provenance tracking. The plan called for a bundled | organization source for display. It was built, then dropped: it answered no question the code asks, and it was a second resolution path over the same two layers that could drift from merge(). The status report already shows whether an organization configuration is applied and its contents.
  • No new environment provider. NavieLanguageModelEnvProvider turned out to emit only APPMAP_API_KEY, with a name and javadoc that no longer described it. Since the API key is now purely identity, the two variables are the same concept, so it became appland.cli.AppMapIdentityEnvProvider rather than gaining a fourth single-variable provider beside it.

Testing

Roughly 60 new or changed tests. Everything except the JCEF and notification surfaces is automated, including the bundled site-config.json layer — AppMapDeploymentTestUtils.withSiteConfigFile writes a real file into the plugin path, which closes the VS Code implementation's largest known coverage gap.

Every non-trivial assertion was checked by deliberately breaking the production code and confirming the right tests go red. That is how the unsound negative assertions above were found, and how the extension-point registration ended up covered from two directions — a unit test of the provider would have passed with a broken <envProvider> entry.

The full manual matrix in the plan has been run: standard and bundled builds, gaining and losing entitlement live, the offline cache fallback, subprocess environment inspection via /proc, and common.customerid against a local Splunk collector.

One matrix line is unverified: an entitled, signed-out user should be eligible for the Copilot discovery notification. The code path is reviewed but nobody has seen the notification appear — it needs a machine with the Copilot plugin installed and authenticated.

Not in this PR

  • The CLI side. appmap-js must honour APPMAP_CUSTOMER_ID — skip authentication, stamp it into telemetry, and implement "real key authenticates, customer ID is attribution only". Until then the subprocess plumbing here has no observable effect. It is fine for that to land later.
  • A pre-existing tool-window bug. After signing out, the AppMap panel is removed but the sign-in panel sometimes doesn't appear, leaving the sidebar blank. It reproduces on main and is not caused by this change, but de-entitlement is a second way to reach it. Investigated and deferred; findings and three ruled-out hypotheses are in the plan commit.
  • SignInViewPanel's JCEF disposal. It never roots its browser and client with Disposer, unlike WebviewEditor, so they leak for the session. Real, unrelated to entitlement, and not the cause of the bug above — worth fixing separately.

There was no authentication gate on anything. A signed-out user with an
appmap.yml got an indexer, a JSON-RPC server and — if the scanner was
enabled by configuration — a scanner, all running behind a sign-in wall
that presents the plugin as inactive. The Tools > AppMap actions stayed
enabled too, and the server-dependent ones only "worked" because the
server was running regardless.

- DefaultCommandLineService: refreshForOpenProjects() computes an empty
  root set when the plugin is inactive. It's a reconciler, so gating the
  root set covers start and stop with one change. start() is guarded too,
  for callers bypassing the refresh path.
- DefaultAppLandJsonRpcService.startServerInternal(): early return with a
  logged reason, in the style of the existing state guards.
- FindingsPanelProvider: gated alongside isScannerEnabled(). Quiescent
  processes produce no new findings, but a stale appmap-findings.json
  would still populate the Problems View "Runtime Analysis" tab; it's the
  one findings surface not behind the tool window swap.
- The eight Tools > AppMap feature actions now extend AppMapFeatureAction,
  whose final update() applies the gate before delegating to an
  overridable updateAvailable(). Deriving the gate rather than repeating
  it makes it structurally impossible to bury it inside an
  isFromActionToolbar() branch, which would leave the Tools menu entry
  enabled. Sign-in, sign-out, Set Organization Configuration and Plugin
  Status Report deliberately stay enabled: they are the routes back into
  an active state.
- StopAppMapRecordingAction widens the gate via isAvailable(): signing out
  mid-recording must not leave a recording running in the user's
  application with no way to stop it.

Not gated: viewing .appmap.json files (the webview editor reads the file
directly), and the CLI binary download, so that the first sign-in doesn't
begin with a wait.

The sign-out half of DefaultCommandLineServiceTest.restartAfterApiKeyChange
asserted that processes stay running after signing out, which this change
contradicts; that coverage moved into processesGatedOnAuthentication.
DefaultAppLandJsonRpcServiceTest signs in during setup, because nearly
every test there needs a running server.

Assisted-by: Claude:claude-opus-5[1m]
…resh

AppMapProjectSettingsPanel had two bugs in how it presented the auto-update
and scanner combo boxes.

It decided whether to offer the null ("Default from deployment settings")
entry for both combos from a single global "are there any deployment
settings at all" check. So a site-config.json which configures only
telemetry made both claim a deployment default they don't have — offering
an entry that defers to nothing, and rendering a comment naming a default
that doesn't exist. Each combo now keys on its own field, and so do the
load and apply paths: selecting the built-in default for a field without a
deployment default stores no override, rather than an override which
happens to match. This also makes adding a field to
AppMapDeploymentSettings.isEmpty() free of side effects on unrelated rows.

Second, the value lists and the comments were built once in getMainPanel(),
so they went stale when an organization configuration was applied or
cleared while the settings page was open — which is driven by the buttons
in that very panel. AppMapProjectConfigurable already reloads the page on
enterpriseDeploymentSettingsChanged, but loadSettingsFrom only assigned
selectedItem. After an apply the combo displayed "Default from deployment
settings" while its list didn't contain that entry, so the value could not
be re-selected; after a Clear the comment kept naming a default which no
longer existed.

Both are fixed by extracting DeploymentBackedSetting. The two rows differed
only in their resource-bundle prefix, their built-in default, and which
deployment field they read, so one parameterised definition covers both.
It has no separate build path — buildRow() populates the row through the
same refresh() the reload uses, so presentation and refresh cannot drift.
The built-in default also unified the four near-identical load/apply
branches, and the comment is now created unconditionally and toggled by
text plus visibility, matching how updateOrgConfigStatus() already handles
the organization-config rows.

Assisted-by: Claude:claude-opus-5[1m]
…ffectiveState

EnterpriseConfigService captured effective state before a mutation and
diffed it afterwards at five sites: the URL-cleared, interactive-failure
and success branches of fetchAndApply, plus clearOrgConfig and
applyLocalFile. Each hand-rolled its own before/after pair per field and
passed booleans to fireSettingsChanged(boolean, boolean), so every new
field of interest means touching all five.

Capture a record either side of the mutation instead, and let
fireSettingsChanged(before, after) do the diffing. It becomes the single
place deciding whether to restart services or rebuild the telemetry
reporter. effectiveTelemetry(), telemetryChanged(),
effectiveScannerEnabled() and both fireSettingsChanged(boolean...)
overloads are gone; the one-arg overload was already dead.

Three simplifications came with it:

- The "Boolean telemetryChanged = null" sentinel is gone. It meant both
  "did telemetry change" and "was anything mutated at all"; the latter is
  now just "before != null".
- The "applied" flag in the success branch was always the same condition,
  so notifyApplied() moved under the same guard.
- applyLocalFile fires on a pooled thread and needed copies of both
  booleans to capture them. Blank finals assigned inside the synchronized
  block are effectively final, so the lambda captures before/after
  directly.

The requirement to read the after-state only once clearSupersededUserOverrides
has run — otherwise a superseded user override isn't reflected — was a
comment on two separate lines and is now structural: "after" is captured
once, at the end of the block.

Behaviour-preserving, so there is no red to show. The existing 41 tests
pass unmodified; the added test characterises a manifest-URL-only apply
(announce the deployment change, but no scanner change and no telemetry
reload) and was confirmed green against the old code first.

Assisted-by: Claude:claude-opus-5[1m]
…ilder

Every field is nullable and they are drawn from only three types, so a
positional constructor call silently accepts a wrong ordering, and adding
a field breaks every call site at once. Both matter here because the class
is about to gain another field.

Add Lombok @builder and make the all-args constructor private, so
positional construction is impossible outside the class rather than merely
discouraged. The one-arg telemetry convenience constructor goes with it,
leaving exactly two ways to construct: new AppMapDeploymentSettings() for
"empty", and the builder. Both are field-name-safe.

merge() now builds by field name, and routes each field through a small
mergeField(enterprise, bundled) helper so its five near-identical ternaries
read alike.

Behaviour-preserving. AppMapDeploymentSettingsTest.jsonSerialization pins
the field set and the Gson mapping, and 110 tests across the packages that
touch deployment settings stay green.

Assisted-by: Claude:claude-opus-5[1m]
…ssor

Phase 1 of the managed-entitlement feature. NOT a standalone change — it
adds a configuration field that nothing reads yet, and is meant to be
squashed with the remaining phases before this branch merges. The "wip"
type keeps it out of the release notes in the meantime.

An administrator-set appMap.customerId entitles a deployment to use the
plugin without a getappmap.com sign-in. In enterprise deployments where
licensing is settled by a B2B agreement, an interactive sign-in is pure
friction: it fails outright under network restrictions and creates audit
burden for a flow that grants nothing the contract has not already
granted. The value is deliberately unverified and must never be described
as an enforcement mechanism — the plugin is open source, so any
client-side check is a business-process boundary.

Resolution needs no new machinery: the field joins AppMapDeploymentSettings
and flows through the existing two-layer merge, with the organization
configuration winning over the bundled site-config.json. There is
deliberately no user-settable equivalent, so no third layer.

Blank collapses to absent — trimmed, with empty or whitespace-only reading
as unset — so a blank organization value falls through to the bundled value
instead of masking it. That rule lives in a hand-written
AppMapDeploymentSettings.getCustomerId(): Gson populates the field
reflectively, so normalizing in a setter or a builder customization would
miss the parsed configuration, which is the only production path. Keeping
it in the single accessor means merge() needs no special case and
Entitlement is a straight delegation.

appland.deployment.Entitlement is the only place that reads the value. It
holds no logic, and is justified as vocabulary for the call sites the later
phases add plus a home for the policy note above.

Deliberately omitted, against the original design: provenance
(bundled vs. organization). Nothing in the resolution needs it, and a
second walk over the two layers can drift from merge(). The status report
already shows whether an organization configuration is applied and what it
contains, so Phase 6 can name the source there if it wants to.

Assisted-by: Claude:claude-opus-5[1m]
Phase 2 of the managed-entitlement feature. NOT a standalone change — to be
squashed with the remaining phases before this branch merges. The "wip"
type keeps it out of the release notes in the meantime.

Adds AppMapApplicationSettings.isSignedInOrEntitled(), which resolves
either a real getappmap.com session or an administrator-set customer ID.
It sits next to isScannerEnabled(), which likewise resolves across the
deployment configuration layer.

Entitlement is a separate axis from credentials: this predicate is for UI
and service state only, and anything needing an actual token keeps using
hasAppMapKey(), because an entitled deployment has none. Every gate that
means "is the plugin active" moves over:

- AppMapFeatureAction.isAppMapAvailable(), which covers all eight
  Tools > AppMap actions in a single line
- DefaultCommandLineService.isServiceStartAllowed()
- DefaultAppLandJsonRpcService.startServerInternal()
- FindingsPanelProvider.create()
- AppMapToolWindowFactory.createContentPanel()
- CopilotStartupNotificationActivity.runActivity()

The Copilot discovery notification is deliberately included rather than
exempt: most entitled deployments are expected to use Copilot, and gated
on a real session they would never learn the integration exists.

isAuthenticated() is deleted. It had a single call site and leaving a
method of that name behind is a trap for the next person gating UI on it.

The indexer, scanner and JSON-RPC server now start for an entitled
deployment with no session, which is the behaviour the VS Code
implementation currently gets wrong: there entitlement unlocks the UI but
no process ever starts.

Note the transition is not yet live — gaining or losing entitlement while
the IDE is running needs a project reload until Phase 5 adds the change
event.

Assisted-by: Claude:claude-opus-5[1m]
Phase 3 of the managed-entitlement feature. NOT a standalone change — to be
squashed with the remaining phases before this branch merges. The "wip"
type keeps it out of the release notes in the meantime.

Rather than adding a provider for the single new variable, this folds it
into the existing one, which turned out to be a single-variable provider
itself: NavieLanguageModelEnvProvider emitted only APPMAP_API_KEY, and
nothing about it was Navie- or language-model-specific any more. Its
javadoc still described "Bring your own key" and "Use hosted AppMap LLM"
and referred to a CopilotAppMapEnvProvider which does not exist.

The API key is now purely identity and non-enterprise usage tracking, so
it and the customer ID are the same concept — who the CLI is acting for.
The class is renamed to appland.cli.AppMapIdentityEnvProvider and moved out
of appland.webviews.navie: it composes from settings and deployment
configuration and belongs to neither, so it sits next to the extension
point it implements. That removes a misleading name instead of adding a
fourth provider beside it.

The two variables stay independent. APPMAP_CUSTOMER_ID is passed whenever
it is set, regardless of authentication state; APPMAP_API_KEY only with a
real session. Neither is faked in the absence of the other — a variable is
omitted rather than sent empty, so the CLI can distinguish "no session"
from "empty session". When both are present the CLI gets both.

order="last" is kept and now has a reason recorded: no other environment
provider may override a credential.

Tested at two layers. The provider unit test covers the five combinations
but constructs the provider directly, so it would pass even with a broken
registration; a second test goes through the real extension point into the
launched command line, and was verified by deleting the <envProvider>
entry.

Assisted-by: Claude:claude-opus-5[1m]
Phase 4 of the managed-entitlement feature. NOT a standalone change — to be
squashed with the remaining phases before this branch merges. The "wip"
type keeps it out of the release notes in the meantime.

Seat usage has to be attributable to the customer whose B2B agreement pays
for it, so the customer ID joins the reporter's common property set.
TelemetryProperties.create() adds "customerid" when entitled and omits it
entirely otherwise — absent rather than present-and-empty — and
withCommonPrefix turns it into common.customerid for both reporter paths,
so neither backend needs to know about the key. The name is all lower-case
with no separator, matching the neighbouring keys and the VS Code plugin.

APPMAP_TELEMETRY_PROPERTIES is deliberately not extended. CliProperties
cherry-picks the four keys the CLI needs; the CLI learns the customer ID
from APPMAP_CUSTOMER_ID and stamps its own events, so it must not arrive
twice.

The reporter is built with a fixed property set, so a change to the
customer ID has to rebuild it or events keep the stale attribution until
the IDE restarts. EffectiveState gains customerId, and fireSettingsChanged
now separates two concerns that shared a branch: the reporter is rebuilt
when telemetry or the customer ID changed, but telemetrySettingsChanged is
fired only for a telemetry change. That event also restarts the processes
which carry telemetry settings in their environment, and driving that from
a customer-ID change would pre-empt the customerIdChanged() event Phase 5
adds.

Note the tests assert that the reporter instance is replaced, not that the
new one carries the ID: the reporter exposes no accessor for its common
properties, and widening its API for a test wasn't worth it. Verifying
common.customerid on real events remains a manual check against a
collector.

Assisted-by: Claude:claude-opus-5[1m]
Phase 5 of the managed-entitlement feature. NOT a standalone change — to be
squashed with the remaining phases before this branch merges. The "wip"
type keeps it out of the release notes in the meantime.

Until now, gaining or losing an entitlement needed a project reload to take
effect. AppMapSettingsListener.customerIdChanged() closes that: it is fired
from EnterpriseConfigService's before/after diff, so no future mutation
site can forget it, and only when the effective value actually changed.
Clearing an organization configuration on a bundled build reconverges on
the bundled ID, which must stay silent or it bounces the CLI processes for
nothing.

Two subscribers. AppMapSettingsReloadProjectListener requests both alarms —
unlike apiKeyChanged(), which only reloads the JSON-RPC server, because the
CLI processes also receive APPMAP_CUSTOMER_ID and, after the quiescence
gate, have to start and stop with the entitlement. AppMapToolWindowFactory
rebuilds its content, swapping the sign-in panel for the AppMap panel just
as signing in does; its existing listener already wraps that in
invokeLater, which is needed because applyLocalFile fires on a pooled
thread.

One of the negative tests was initially unsound: it waited on
enterpriseDeploymentSettingsChanged, which fireSettingsChanged publishes
first, then asserted about customerIdChanged, published last — so it could
assert before the event it was checking for, and passed even when the event
fired unconditionally. It now drives the change through clearOrgConfig(),
which fires synchronously, so returning from it is a real barrier.

Also replaces the fully-qualified appland.deployment.Entitlement references
introduced in earlier phases with an import.

Assisted-by: Claude:claude-opus-5[1m]
… fail

Several negative assertions in EnterpriseConfigServiceTest could not fail.
They applied an organization configuration, waited for
enterpriseDeploymentSettingsChanged, then asserted that some other event
had not been fired. But applyLocalFile dispatches its listeners on a pooled
thread, and fireSettingsChanged publishes enterpriseDeploymentSettingsChanged
first and the conditional events after it — so the assertion could run
before the event it was checking for, and passed whether or not the event
fired.

This was found by breaking the production code on purpose: making
customerIdChanged fire unconditionally left
customerIdChanged_notFiredForAnUnrelatedChange green.

The mutation under test is now clearOrgConfig(), which fires its listeners
synchronously, so returning from it is a real barrier: every listener has
run. Two helpers make that explicit — applyAndAwait() for setup, which also
waits out the setup's own asynchronous events so they can't pollute a later
subscription, and clearAndAwait() for the observed change.

Renamed the two tests accordingly, since they no longer exercise
applyLocalFile. Verified by firing the scanner, telemetry and reporter-reload
paths unconditionally: all three negatives now go red, where two of them
previously stayed green.

Assisted-by: Claude:claude-opus-5[1m]
Phase 6, the last of the managed-entitlement feature. NOT a standalone
change — to be squashed with the other phases before this branch merges.
The "wip" type keeps it out of the release notes in the meantime.

Administrators need to confirm a rollout landed, so the Plugin Status
Report gains "Managed entitlement: active" and the customer ID, or
"Managed entitlement: not configured". The ID is printed unredacted,
unlike the Splunk token: it is not a secret, and redacting it would defeat
the point of the report.

Settings > Tools > AppMap > Advanced shows the same value read-only. There
is deliberately no editable field: the customer ID is a deployment
configuration key with no user-settable equivalent, and the row says so.
It is hidden entirely when there is no entitlement, and refreshed on reload
so it follows an apply or clear made from the buttons a few rows above it.
It also initialises when the panel is built rather than only on the first
reload, so it can't be briefly visible and empty.

ORGANIZATION_CONFIGURATION.md documents the key in the JSON example, the
key table and the verification section, plus a section on what it does. Two
points are stated plainly there, because an undocumented switch that
disables authentication reads as a backdoor to exactly the auditors this
feature accommodates: it is not a licence key and not an enforcement
mechanism, and it is not a secret. The "Clearing" section gained the
bundled-build case, where clearing falls back to the bundled ID instead of
de-entitling.

Assisted-by: Claude:claude-opus-5[1m]
Temporary commit to make review easier. This is an internal working
document, not something to ship: the other plan docs in this repo
(remote-config-plan.md and friends) are deliberately untracked, and this
one should be too.

It carries the design reasoning, the record of what was actually built
versus what was originally specified, and the test matrix with each line
marked automated or manual. Useful for reviewing the wip(entitlement)
commits; delete this commit when squashing them.

Assisted-by: Claude:claude-opus-5[1m]
@dividedmind
dividedmind requested review from kgilpin and a lite review from Copilot August 20, 2026 12:59
@dividedmind dividedmind self-assigned this Aug 20, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a “managed entitlement” mechanism (appMap.customerId) that lets deployments treat the plugin as signed-in without an interactive getappmap.com login, while keeping real credentials (APPMAP_API_KEY) and entitlement attribution (APPMAP_CUSTOMER_ID, common.customerid) as separate concerns.

Changes:

  • Introduces customerId in deployment settings + Entitlement accessor, merges across bundled/organization layers, and normalizes blank-to-absent.
  • Gates UI/actions/services on isSignedInOrEntitled() and propagates entitlement into subprocess env + telemetry, with live updates via a new customerIdChanged() event.
  • Updates settings/status/docs and adds broad automated test coverage for entitlement behavior and quiescent signed-out state.

Reviewed changes

Copilot reviewed 48 out of 48 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
plugin-core/src/test/java/appland/telemetry/TelemetryServiceTest.java Updates test to use deployment settings builder.
plugin-core/src/test/java/appland/telemetry/TelemetryPropertiesTest.java Adds coverage for customerid property behavior and CLI JSON exclusion.
plugin-core/src/test/java/appland/telemetry/splunk/AppMapSplunkTelemetryCliEnvProviderTest.java Updates tests to use deployment settings builder.
plugin-core/src/test/java/appland/settings/AppMapProjectSettingsPanelTest.java New tests for per-field deployment defaults + customer ID row visibility.
plugin-core/src/test/java/appland/settings/AppMapApplicationSettingsTest.java Tests isSignedInOrEntitled() across session/entitlement combinations.
plugin-core/src/test/java/appland/rpcService/DefaultAppLandJsonRpcServiceTest.java Adds tests for auth/entitlement gating and env propagation into command line.
plugin-core/src/test/java/appland/problemsView/FindingsPanelProviderTest.java New tests ensuring findings tab is gated but enabled for entitled deployments.
plugin-core/src/test/java/appland/enterpriseConfig/EnterpriseConfigServiceTest.java Adds tests around customerId change events and reporter rebuild semantics.
plugin-core/src/test/java/appland/deployment/EntitlementTest.java New tests for entitlement resolution, trimming, blank-as-absent, malformed config.
plugin-core/src/test/java/appland/deployment/AppMapDeploymentSettingsTest.java Adds parsing/isEmpty coverage for appMap.customerId; migrates to builder.
plugin-core/src/test/java/appland/deployment/AppMapDeploymentSettingsServiceTest.java Adds merge/precedence/blank handling tests for customer ID.
plugin-core/src/test/java/appland/cli/DefaultCommandLineServiceTest.java Adds tests to ensure CLI processes are quiescent while inactive and start on entitlement.
plugin-core/src/test/java/appland/cli/DefaultAppLandDownloadServiceTest.java Updates deployment settings construction to builder.
plugin-core/src/test/java/appland/cli/AppMapIdentityEnvProviderTest.java New unit tests for env variable omission/presence semantics.
plugin-core/src/test/java/appland/actions/PluginStatusTest.java Adds assertions for entitlement lines and customer ID presence in status report.
plugin-core/src/test/java/appland/actions/AppMapActionAuthenticationGateTest.java New tests verifying Tools menu action gating and exceptions (login/status/etc.).
plugin-core/src/test/java/appland/actions/AddNavieContextFilesActionTest.java Ensures test signs in so JSON-RPC server prerequisite is met.
plugin-core/src/main/resources/META-INF/appmap-core.xml Replaces env provider registration with AppMapIdentityEnvProvider.
plugin-core/src/main/resources/messages/appland.properties Adds strings for Customer ID row in settings.
plugin-core/src/main/kotlin/appland/settings/AppMapProjectSettingsPanel.kt Refactors deployment-backed combos, adds customer ID read-only row + refresh logic.
plugin-core/src/main/java/appland/webviews/navie/NavieLanguageModelEnvProvider.java Removes old API-key-only env provider (superseded by identity provider).
plugin-core/src/main/java/appland/toolwindow/AppMapToolWindowFactory.java Tool window content now rebuilds on customerIdChanged() and uses isSignedInOrEntitled().
plugin-core/src/main/java/appland/telemetry/TelemetryProperties.java Adds CUSTOMER_ID property and emits it when entitled.
plugin-core/src/main/java/appland/settings/AppMapSettingsReloadProjectListener.java Restarts services on customerIdChanged() via existing debounced alarms.
plugin-core/src/main/java/appland/settings/AppMapSettingsListener.java Adds customerIdChanged() listener event.
plugin-core/src/main/java/appland/settings/AppMapApplicationSettings.java Adds isSignedInOrEntitled() (replacing prior auth predicate usage).
plugin-core/src/main/java/appland/rpcService/TestAppLandJsonRpcService.java Adds deterministic hasJsonRpcProcess() helper for gating tests.
plugin-core/src/main/java/appland/rpcService/DefaultAppLandJsonRpcService.java Gates JSON-RPC server start on isSignedInOrEntitled().
plugin-core/src/main/java/appland/problemsView/FindingsPanelProvider.java Gates problems-view tab on signed-in-or-entitled + scanner enabled.
plugin-core/src/main/java/appland/enterpriseConfig/EnterpriseConfigService.java Refactors to before/after EffectiveState; reloads reporter and fires customerIdChanged().
plugin-core/src/main/java/appland/deployment/Entitlement.java New single read-path for effective customer ID / entitlement predicate.
plugin-core/src/main/java/appland/deployment/AppMapDeploymentSettingsService.java Builder-based merge; includes customerId field.
plugin-core/src/main/java/appland/deployment/AppMapDeploymentSettings.java Adds customerId, builder-only construction, and blank-to-absent accessor normalization.
plugin-core/src/main/java/appland/cli/DefaultCommandLineService.java Gates CLI service startup/reconciliation on isSignedInOrEntitled().
plugin-core/src/main/java/appland/cli/AppMapIdentityEnvProvider.java New env provider emitting APPMAP_API_KEY and/or APPMAP_CUSTOMER_ID.
plugin-core/src/main/java/appland/actions/StopAppMapRecordingAction.java Applies feature-surface gate while preserving ability to stop active recordings.
plugin-core/src/main/java/appland/actions/StartAppMapRecordingAction.java Applies feature-surface gate to prevent starting recordings while inactive.
plugin-core/src/main/java/appland/actions/SetNavieOpenAiKeyAction.java Moves under AppMapFeatureAction gating.
plugin-core/src/main/java/appland/actions/QuickReviewAction.java Moves under AppMapFeatureAction gating; keeps project availability check.
plugin-core/src/main/java/appland/actions/PluginStatus.java Adds entitlement/customer ID section to status report output.
plugin-core/src/main/java/appland/actions/OpenNavieThreadAction.java Moves under AppMapFeatureAction gating; keeps project availability check.
plugin-core/src/main/java/appland/actions/OpenAppMapNavieAction.java Moves under AppMapFeatureAction gating.
plugin-core/src/main/java/appland/actions/GenerateOpenApiAction.java Moves under AppMapFeatureAction gating.
plugin-core/src/main/java/appland/actions/ChooseAndAddNavieContextFilesAction.java Moves under AppMapFeatureAction gating; availability requires active Navie editor.
plugin-core/src/main/java/appland/actions/AppMapFeatureAction.java New base action implementing centralized “feature surface” enablement gate.
plugin-copilot/src/main/java/appland/copilotChat/CopilotStartupNotificationActivity.java Makes notification eligibility follow signed-in-or-entitled state.
PLAN-customer-id.md Adds internal plan doc (PR description indicates it should be dropped before merge).
ORGANIZATION_CONFIGURATION.md Documents appMap.customerId semantics, non-secret status, and clearing behavior.

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

Comment thread PLAN-customer-id.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants