InfraBench

nplus1 × Zendo

Published attempt

0.96 · Partial reward

v9m-rl-learnability-tp8 via opencode

Attempt scoring

Weighted checks

  • Failed
    core engine and unrelated capabilities are unchanged
    Reward weight 3%
  • Passed
    web-job-runner resolves its prerequisites without infra
    Reward weight 5%
  • Passed
    Next tracing includes the project job roster
    Reward weight 5%
  • Passed
    plain web keeps the shared seam and project-owned routes
    Reward weight 4%
  • Passed
    standalone documents optional credentials and fail-closed use
    Reward weight 5%
  • Passed
    infra derives a valid suffix-safe runner identity
    Reward weight 5%
  • Passed
    Terraform grants the runner exactly the two-job roster
    Reward weight 12%
  • Passed
    runner key and Terraform outputs have the required semantics
    Reward weight 6%
  • Passed
    Vercel receives region and sensitive runner credentials
    Reward weight 6%
  • Passed
    local env workflow writes both values only when configured
    Reward weight 7%
  • Passed
    consumer composition is independent of requested plugin order
    Reward weight 5%
  • Passed
    shared policy has one neutral owner and both legacy moves
    Reward weight 7%
  • Passed
    removing runner preserves the Gemini shared owner
    Reward weight 4%
  • Passed
    removing Gemini preserves the runner shared owner
    Reward weight 4%
  • Passed
    removing both consumers cleans the shared owner
    Reward weight 4%
  • Passed
    projects without credential consumers stay isolated
    Reward weight 5%
  • Passed
    repeated apply preserves project-owned roster, route, and edits
    Reward weight 5%
  • Passed
    runner cleanup is complete while Gemini and project files remain
    Reward weight 4%
  • Failed
    downstream lint fixture exercises the generated capability
    Reward weight 1%
  • Passed
    repository smoke detects broken runner contracts
    Reward weight 3%

The official reward is authoritative; verifier hard gates can override the visible sum of checks.

Agent input

Instruction

# Add a composable web Cloud Run job-runner capability to n1x

Introduce a `web-job-runner` capability that requires `web`, `backend-jobs`,
and a new shared `gcp-service-account-keys` prerequisite. It should make
generated web apps able to *host* Cloud Run job triggering by composing
identity, environment, guidance, and Next.js bundling around the existing
project-owned `backend/jobs.yaml` roster.

Application route handlers under `web/src/app/api/jobs/**` are project-owned
and out of scope. The capability must not create, seed, replace, or delete
those routes. Existing route files must survive repeated apply operations and
removal of `web-job-runner` unchanged.

The capability must contribute:

- Optional `JOB_RUNNER_REGION` and `JOB_RUNNER_CREDENTIALS_JSON_BASE64`
  placeholders in the generated web environment example, attributed to the
  plugin. Unconfigured environments must keep the app usable; triggering is
  optional rather than a hard dependency.
- Generated guidance that allowlisted job names come from `backend/jobs.yaml`,
  that Terraform-managed credentials are written locally by `just web env`
  after `just infra apply`, that unconfigured triggering should fail closed
  (for example HTTP 501), and that jobs themselves must stay full-overwrite
  idempotent.
- Effective Next.js serverless tracing for `/api/jobs/**` that includes
  `../backend/jobs.yaml` and sets the tracing root to the repository parent of
  `web/`. Use `outputFileTracingRoot` plus `outputFileTracingIncludes`; the
  equivalent valid TypeScript expression and property order are not
  prescribed.

Make the Next.js config entrypoint composable. The `web` capability should own
a generic marker-based `next.config.ts` seam, while `web-job-runner`
contributes the jobs.yaml tracing configuration through that seam. Projects
that do not select `web-job-runner` must keep the shared marker without gaining
job-runner settings.

When `infra` is also selected, provision a dedicated job-runner identity and
wire it into the existing web environment composition:

- Create a valid Google service-account id derived from the app id that is at
  most 30 characters and always retains the complete `-job-runner` suffix,
  including for long app ids.
- Grant that identity `roles/run.invoker` separately on exactly the jobs in
  `local.backend_jobs` / `module.jobs`, so the IAM roster cannot drift from
  `backend/jobs.yaml`. Do not add a project-level, wildcard, or other broader
  `roles/run.invoker` grant. The implementation must handle a nonempty roster
  with at least two jobs; it must not rely on the starter roster being empty.
- Mint a service-account key for the runner identity and expose region plus
  base64 credentials as Terraform outputs. A service-account email output is
  optional and is not part of the required contract.
- Pass `JOB_RUNNER_REGION` and `JOB_RUNNER_CREDENTIALS_JSON_BASE64` into Vercel
  through the existing `# n1x:vercel-env-vars` composition point, and into
  local env files through `# n1x:web-env-values` using optional writers.

## Share service-account key policy ownership

Add a neutral `gcp-service-account-keys` prerequisite plugin and make both the
existing `gemini` capability and `web-job-runner` require it. The prerequisite
must use an `infra` integration rather than requiring `infra` itself, so both
consumer capabilities still work without generating infrastructure when
`infra` is absent.

With `infra`, `gcp-service-account-keys` must be the sole declarative owner of:

- the `orgpolicy.googleapis.com` project-service resource; and
- the project override for `iam.disableServiceAccountKeyCreation` with
  `enforced = false`.

Declare the Google provider through the existing Terraform provider marker.
Remove the duplicate org-policy API and policy ownership from Gemini and keep
both the Gemini and job-runner key resources dependent on the one shared
policy resource. Preserve existing Gemini Terraform state with declarative
`moved` blocks from
`google_project_service.gemini_apis["orgpolicy.googleapis.com"]` and
`google_project_organization_policy.gemini_service_account_key_creation` to
their new neutral shared addresses; changing generated files must not propose
destroying and recreating those existing remote objects.

The shared ownership lifecycle must be stable and independent of requested
plugin order:

- Gemini plus `infra` renders exactly one org-policy API owner and one key
  creation override.
- `web-job-runner` plus `infra` renders exactly the same single shared owners.
- Selecting both consumers still renders each shared object exactly once at
  the same Terraform addresses.
- Removing either consumer while the other remains preserves those addresses
  and objects.
- Removing both consumers removes the shared managed Terraform and resolved
  prerequisite cleanly.

## Isolation and lifecycle

- `web-job-runner` must work without `infra`: guidance, optional env docs, and
  Next.js bundling still apply, while no `infra/` tree is introduced.
- Projects that do not select `web-job-runner` must not gain job-runner
  Terraform, env declarations, guidance, or Next.js tracing includes.
- Projects that select neither credential consumer must not gain the shared
  key-policy Terraform merely from `backend-jobs` or `infra`.
- Repeated apply operations must be idempotent and must preserve project-owned
  edits outside managed paths, including a nonempty multi-job roster and
  project-owned job routes.
- Removing `web-job-runner` must clean its managed Terraform, environment
  declarations, guidance, and Next.js contribution without removing the
  shared `next.config.ts` marker owned by `web`, without disturbing
  `backend-jobs`, and without removing shared key-policy infrastructure still
  needed by Gemini.

Implement this through n1x's existing declarative plugin actions. The core
engine should not change. Refactoring the existing `gemini` capability and the
`web` Next.js config template is in scope; unrelated capabilities should not
need changes.

Update downstream lint and smoke coverage so the new capability, shared
prerequisite, multi-consumer composition, and clean removal are exercised while
preserving all existing behavior.