Problem
The gapp:deploy skill's Phase 0: Assess the Situation cannot
distinguish a CI-deployed solution from a never-deployed one,
and actively routes the agent toward a local setup → deploy on a
solution that is already live via CI/CD.
Phase 0 instructs the agent to assess using only two calls:
Neither reveals CI state. gapp_status is documented as "local, fast"
— it reads local machine state. For a solution that was deployed from
CI (or simply from a different machine), the local checkout never ran
gapp setup, so gapp_status returns:
{
"initialized": true,
"deployment": { "project": null, "pending": true, "services": [] },
"next_step": { "action": "setup", "hint": "No GCP project attached..." }
}
This is indistinguishable from a solution that was never deployed.
The skill's own "Solution Lifecycle" table then maps that exact signal
("Initialized, no project" → next_step.action: setup) to "run
gapp setup <project-id>," and the deploy phase follows with
gapp deploy. Following the skill as written leads the agent to
attempt a fresh local setup + deploy against a solution that is
already CI-deployed and healthy.
A gapp_ci_status tool exists and is listed in the skill's MCP Tools
Reference table, but Phase 0 never instructs the agent to call it,
and nothing in the assessment flow cross-checks it. The one tool that
would disambiguate local-deployed from CI-deployed is omitted from the
one step whose entire job is disambiguation.
Impact
An agent assisting with "deploy the latest version" on a CI-deployed
solution will, by following the skill:
- Read
gapp_status, see project: null / next_step: setup.
- Conclude the solution has never been deployed.
- Attempt
gapp_setup (provisioning/binding infrastructure locally)
and gapp_deploy (a local terraform/docker build path).
This diverges from how the solution actually ships (CI), can trip
infrastructure-mutation permission gates, and risks creating a second,
machine-local deploy path for a solution whose source of truth is the
CI pipeline. The correct action — gapp_ci_trigger — is never reached
because the assessment never establishes that CI is the deploy channel.
How it surfaced
While deploying a new version of an mcp-app solution, the agent
followed Phase 0 as written:
gapp_status reported project: null, pending: true,
next_step.action: setup — local state had no project binding.
gapp_list(all_owners=True) showed the same solution already
deployed to a project, contradicting gapp_status. This
contradiction (status says "never deployed," list says "deployed
here") is itself the tell, but the skill offers no guidance to
resolve it and the lifecycle table sided with gapp_status.
- The agent attempted
gapp_setup (denied by an infra-mutation
permission gate) and gapp_deploy (errored with a NoneType
resolution failure because no project was bound locally).
- Only after stepping outside the skill's prescribed Phase 0 and
calling gapp_ci_status did the true state become clear:
{ "repo": "<ci-repo>", "workflow": true } — the solution is
CI-wired. gapp_ci_trigger(ref=<tag>) then deployed successfully.
The CI deploy path worked perfectly once reached. The defect is
purely in the assessment guidance, which omits the CI check and
trusts a local-only signal as authoritative for global deploy state.
Proposed solution
Make CI state a first-class input to Phase 0 assessment, and teach the
skill to treat a gapp_status / gapp_list contradiction as a signal
rather than resolving it in favor of local state.
Specifically:
-
Add gapp_ci_status to the Phase 0 assessment calls. Phase 0
should call gapp_list, gapp_status, and gapp_ci_status (in
parallel) before concluding anything about deploy state.
-
Add a disambiguation rule. When gapp_status reports
project: null / next_step: setup but gapp_list shows the
solution already deployed to a project (or gapp_ci_status reports
workflow: true), the solution is deployed elsewhere (CI or
another machine), not un-deployed. The correct next action is to
redeploy via the established channel — gapp_ci_trigger when CI is
wired — not local setup + deploy.
-
Update the Solution Lifecycle table so the "Initialized, no
project" row notes that this local signal does not imply
"never deployed" — it must be cross-checked against gapp_list
and gapp_ci_status. Local state reflects the local machine only.
-
Establish a CI-first preference in the redeploy guidance. When
gapp_ci_status shows the solution is CI-wired, the skill should
prefer gapp_ci_trigger for redeploys and treat local gapp_deploy
as the fallback for non-CI solutions, rather than presenting local
deploy as the default path.
Work breakdown
Problem
The
gapp:deployskill's Phase 0: Assess the Situation cannotdistinguish a CI-deployed solution from a never-deployed one,
and actively routes the agent toward a local
setup→deployon asolution that is already live via CI/CD.
Phase 0 instructs the agent to assess using only two calls:
gapp_listgapp_statusNeither reveals CI state.
gapp_statusis documented as "local, fast"— it reads local machine state. For a solution that was deployed from
CI (or simply from a different machine), the local checkout never ran
gapp setup, sogapp_statusreturns:{ "initialized": true, "deployment": { "project": null, "pending": true, "services": [] }, "next_step": { "action": "setup", "hint": "No GCP project attached..." } }This is indistinguishable from a solution that was never deployed.
The skill's own "Solution Lifecycle" table then maps that exact signal
("Initialized, no project" →
next_step.action: setup) to "rungapp setup <project-id>," and the deploy phase follows withgapp deploy. Following the skill as written leads the agent toattempt a fresh local setup + deploy against a solution that is
already CI-deployed and healthy.
A
gapp_ci_statustool exists and is listed in the skill's MCP ToolsReference table, but Phase 0 never instructs the agent to call it,
and nothing in the assessment flow cross-checks it. The one tool that
would disambiguate local-deployed from CI-deployed is omitted from the
one step whose entire job is disambiguation.
Impact
An agent assisting with "deploy the latest version" on a CI-deployed
solution will, by following the skill:
gapp_status, seeproject: null/next_step: setup.gapp_setup(provisioning/binding infrastructure locally)and
gapp_deploy(a local terraform/docker build path).This diverges from how the solution actually ships (CI), can trip
infrastructure-mutation permission gates, and risks creating a second,
machine-local deploy path for a solution whose source of truth is the
CI pipeline. The correct action —
gapp_ci_trigger— is never reachedbecause the assessment never establishes that CI is the deploy channel.
How it surfaced
While deploying a new version of an mcp-app solution, the agent
followed Phase 0 as written:
gapp_statusreportedproject: null,pending: true,next_step.action: setup— local state had no project binding.gapp_list(all_owners=True)showed the same solution alreadydeployed to a project, contradicting
gapp_status. Thiscontradiction (status says "never deployed," list says "deployed
here") is itself the tell, but the skill offers no guidance to
resolve it and the lifecycle table sided with
gapp_status.gapp_setup(denied by an infra-mutationpermission gate) and
gapp_deploy(errored with aNoneTyperesolution failure because no project was bound locally).
calling
gapp_ci_statusdid the true state become clear:{ "repo": "<ci-repo>", "workflow": true }— the solution isCI-wired.
gapp_ci_trigger(ref=<tag>)then deployed successfully.The CI deploy path worked perfectly once reached. The defect is
purely in the assessment guidance, which omits the CI check and
trusts a local-only signal as authoritative for global deploy state.
Proposed solution
Make CI state a first-class input to Phase 0 assessment, and teach the
skill to treat a
gapp_status/gapp_listcontradiction as a signalrather than resolving it in favor of local state.
Specifically:
Add
gapp_ci_statusto the Phase 0 assessment calls. Phase 0should call
gapp_list,gapp_status, andgapp_ci_status(inparallel) before concluding anything about deploy state.
Add a disambiguation rule. When
gapp_statusreportsproject: null/next_step: setupbutgapp_listshows thesolution already deployed to a project (or
gapp_ci_statusreportsworkflow: true), the solution is deployed elsewhere (CI oranother machine), not un-deployed. The correct next action is to
redeploy via the established channel —
gapp_ci_triggerwhen CI iswired — not local
setup+deploy.Update the Solution Lifecycle table so the "Initialized, no
project" row notes that this local signal does not imply
"never deployed" — it must be cross-checked against
gapp_listand
gapp_ci_status. Local state reflects the local machine only.Establish a CI-first preference in the redeploy guidance. When
gapp_ci_statusshows the solution is CI-wired, the skill shouldprefer
gapp_ci_triggerfor redeploys and treat localgapp_deployas the fallback for non-CI solutions, rather than presenting local
deploy as the default path.
Work breakdown
gapp_ci_statusto the Phase 0 assessment step inskills/deploy/SKILL.md(call it alongsidegapp_listandgapp_status).project: null/next_step: setupsignal combined with thesolution appearing in
gapp_list(orworkflow: truefromgapp_ci_status) means "deployed elsewhere," not "neverdeployed."
row to flag that the local signal is not authoritative for global
deploy state and must be cross-checked.
gapp_ci_triggerwhen thesolution is CI-wired; local
gapp_deployis the fallback fornon-CI solutions.
gapp_statusoutput (e.g., a field indicating the solution isknown-deployed in a project per labels even when local state has
no binding), so the local-only signal is less misleading at the
source. Track separately if it warrants its own issue.