nplus1 × Zendo
Published attempt
0.58 · Partial reward
gpt-5.6-sol via opencode
Attempt scoring
Weighted checks
- Failedcore engine and unrelated capabilities are unchangedReward weight 3%
- Passedweb-job-runner resolves its prerequisites without infraReward weight 5%
- PassedNext tracing includes the project job rosterReward weight 5%
- Passedplain web keeps the shared seam and project-owned routesReward weight 4%
- Failedstandalone documents optional credentials and fail-closed useReward weight 5%
- Failedinfra derives a valid suffix-safe runner identityReward weight 5%
- FailedTerraform grants the runner exactly the two-job rosterReward weight 12%
- Failedrunner key and Terraform outputs have the required semanticsReward weight 6%
- FailedVercel receives region and sensitive runner credentialsReward weight 6%
- Passedlocal env workflow writes both values only when configuredReward weight 7%
- Passedconsumer composition is independent of requested plugin orderReward weight 5%
- Passedshared policy has one neutral owner and both legacy movesReward weight 7%
- Passedremoving runner preserves the Gemini shared ownerReward weight 4%
- Failedremoving Gemini preserves the runner shared ownerReward weight 4%
- Passedremoving both consumers cleans the shared ownerReward weight 4%
- Passedprojects without credential consumers stay isolatedReward weight 5%
- Passedrepeated apply preserves project-owned roster, route, and editsReward weight 5%
- Passedrunner cleanup is complete while Gemini and project files remainReward weight 4%
- Faileddownstream lint fixture exercises the generated capabilityReward weight 1%
- Passedrepository smoke detects broken runner contractsReward weight 3%
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.