Task Breakdown: App env & dependencies
Ordered tasks derived from
plan.md. Each is small enough to land in one PR and ships with tests per Constitution VI. The schema task ships its migration in the same PR per Constitution V.
Epic ID: APW-07-app-env-and-dependencies
Spec: ./spec.md · Plan: ./plan.md
Status: Draft
Last updated: 2026-09-17
How to use
- Tasks are sequential by default.
(parallel)means it may run alongside its predecessor. - Every task names the exact files to create or modify. An implementer should never have to guess a path. Modify
paths not marked "(create if absent)" exist on
develop@ee45946e5. - Every task has a Test line (a file and what it asserts, or the command that is the test) and a Done when
line that is checkable without reading the diff.
(ACC-07-nn)tags name the spec §8 criteria a Test line proves. - Add new tasks at the bottom rather than renumbering.
- Phase boundaries are ship boundaries:
developmust be green and deployable at the end of each phase. - Repo commands run from the monorepo root unless a task says otherwise. Migrations are authored from
apps/api/. Package filters:@ever-works/contracts,@ever-works/plugin,@ever-works/agent,@ever-works/k8s-plugin,ever-works-api,ever-works-web,@ever-works/trigger-tasks,@ever-works/app-dependencies-external-plugin(new, T22),@ever-works/apps-tier-dependencies-plugin(new, T36). - API behaviour is tested under
apps/api/src/**orapps/web/e2e/— neverapps/api/test/(Resolution R-22). - Never modify
packages/agent/src/services/work-runtime-env.constants.ts,work-runtime-env.service.ts,docs/runbooks/WORK_RUNTIME_ENV.mdorapps/web/src/components/works/detail/deploy/RuntimeEnvManagement.tsx. - Cross-epic prerequisites (typed fakes until they land): APW-03 effective App spec +
app.spec.appliedand thegenerate.keypair.formatschema field (R-11); APW-05's latest deployable Build with itsbuildValueFingerprintsmap (APW07-G03); APW-06'sAppRuntimeTargetPort.prepareDependencyTarget(the runtime target resolver —{ kubeconfig, context, namespace, appPodLabels }, plus theunavailablereasons), theAppRuntimeEnvSourceport withtarget,fingerprintsandresolveEphemeral(…, { dependencyOutputs }), APW-06's current DeploymentappRender.envFingerprints, domain state, thedelete-app-work,prepare-namespaceandverification-deployops (R-15, R-10, GAP-06) and its calls toreconcile/ensureReadyForDeploy/onAppRemoved/list/provisionEphemeral(APW-06 T69, T70, T58, T60); APW-01'sWorkCapabilities.appEnvironment(R-7) andAppWorkAccessService.resolve(APW07-G13); APW-10'sAppsTierPolicy(R-5) andIAppsTierProvider.setDependencies/releaseDependenciesplus theWork.status.dependencies[]contract (GAP-22, APW10-G01).
Phase P1 — App env, Your-cluster and external dependencies
Delivers spec FR-1…FR-50 and FR-56…FR-60: the Environment table, secrecy, generators and keypair formats, validation,
resolution, gating, .env import, dependency providers on Your cluster and external servers, lifecycle and backup
states, App Work deletion and verification values.
P1.1 — Contracts
-
T1. App env contracts. Create
packages/contracts/src/apps/app-env.tswithAPP_ENV_ORIGINS,APP_ENV_PHASES,APP_ENV_GENERATOR_KINDS,APP_ENV_ALPHABETS(exact strings from plan §3.3),APP_ENV_KEYPAIR_TYPES,APP_ENV_KEYPAIR_FORMATS,APP_ENV_KEYPAIR_RAW_TYPES,APP_ENV_NAME_PATTERN,APP_ENV_RESERVED_PREFIX,APP_ENV_PUBLIC_PREFIXES, the numeric constants andAppEnvEntryView. Modify APW-03's barrelpackages/contracts/src/apps/index.tsto export it (create the barrel and its export frompackages/contracts/src/index.tsonly if APW-03 has not landed — R-1). Test:packages/contracts/src/apps/__tests__/app-env.spec.ts— alphabet lengths 62 / 76 / 16 / 64 with no duplicate characters;alnum-symbolscontains no double quote, single quote, backtick,$or space;APP_ENV_KEYPAIR_FORMATSis exactlypem,base64url-raw,pkcs12; every numeric constant (65_536,1_048_576,300,16_384,50,65_536,500,60_000,10,30,10). Done when:pnpm --filter @ever-works/contracts testis green andpnpm --filter @ever-works/contracts buildemits declarations. -
T2 (parallel with T1). App dependency contracts. Create
packages/contracts/src/apps/app-dependencies.tswithAPP_DEPENDENCY_KINDS,APP_DEPENDENCY_OUTPUTS, deadlines, sizes, retry, refresh, backup-overdue, relay limit,APP_DEPENDENCY_MANAGED, theAppDependencyViewtype and every status, status-reason and API error code as a constant (APP_DEPENDENCY_STATUSESincl.awaitingConfig,APP_DEPENDENCY_REASONS,APP_ENV_ERROR_CODESinapp-env.ts) so a code added without copy fails the key test below (APW07-G23). Test:packages/contracts/src/apps/__tests__/app-dependencies.spec.ts— output names and secret flags equal spec FR-40 exactly;APP_DEPENDENCY_READY_DEADLINE_MS= 600,000 / 300,000 / 600,000 / 30,000;APP_DEPENDENCY_BACKUP_OVERDUE_MS= 93,600,000; every reason and error-code constant resolves to exactly one message key underdashboard.workDetail.appDependencies.*/…appEnv.errors.*inapps/web/messages/en.json(APW07-G23). Done when:pnpm --filter @ever-works/contracts testis green. -
T3 (parallel with T1).
app-dependencycapability and category. Createpackages/plugin/src/contracts/capabilities/app-dependency.interface.tsexactly as plan §4.7, includingAppDependencyContext.ephemeral, thestopWorkloadsdeprovision option andisAppDependencyProvider, plus theResourceRefstype the rest of the file references ({ namespace?: string; objects: Array<{ kind: string; name: string }> ≤ 20; databases?: string[]; buckets?: string[] }— added, APW07-G24: plan §4.7 named the type without defining it). Modifypackages/plugin/src/contracts/capabilities/index.ts(export),packages/plugin/src/contracts/facade-capabilities.ts(APP_DEPENDENCY: 'app-dependency'),packages/plugin/src/contracts/plugin-manifest.types.ts(append'app-dependency'). Test:packages/plugin/src/contracts/__tests__/app-dependency-capability.spec.ts— validity helpers, type guard, no existing capability/category removed. Done when:pnpm --filter @ever-works/plugin testis green. -
T4 (parallel with T1).
appEnvironmentWork capability — consume only. No change topackages/contracts/src/domain/work-capabilities.ts: APW-01 T3 addsreadonly appEnvironment: boolean(trueforapp,falsefor every other kind) and its spec pins (Resolution R-7). This epic only readsgetWorkCapabilities(work.kind).appEnvironment(T27's sub-tabs) and never declares or defaults the field. Test:git grep -n "appEnvironment" packages/contracts/src/domain/work-capabilities.tsshows APW-01's field, and no APW-07 change touches that file or its spec. Done when: APW-01 T3 is merged and web, API and agent type-check against it.
P1.2 — Entities, migration, repositories
-
T5.
WorkAppEnvValueentity. Createpackages/agent/src/entities/work-app-env-value.entity.ts(plan §3.1). Modifypackages/agent/src/entities/index.ts,packages/agent/src/database/_entity-names.ts('WorkAppEnvValue'),packages/agent/src/database/_entities-inventory.ts. Test:packages/agent/src/entities/__tests__/work-app-env-value.entity.spec.ts— unique index,valueEncryptedNOT NULL, scope columns present, noselect: truedefault exposure ofvalueEncryptedin repositoryfindhelpers. Done when:pnpm --filter @ever-works/agent test -- work-app-env-value.entityis green and the drift specspackages/agent/src/database/database.module.spec.tsanddatabase.config.spec.tspass. -
T6 (parallel with T5).
WorkAppDependencyentity. Createpackages/agent/src/entities/work-app-dependency.entity.ts(plan §3.2); register in the same three files ('WorkAppDependency'). Test:packages/agent/src/entities/__tests__/work-app-dependency.entity.spec.ts— partial unique indexWHERE status NOT IN ('kept','deleted'), defaults (attempts0,outputsVersion0,inSpectrue). Done when:pnpm --filter @ever-works/agent test -- work-app-dependency.entityis green and the drift specs pass. -
T7. Migration. Create
apps/api/src/migrations/1792070000000-CreateAppEnvAndDependencies.ts(generate the skeleton fromapps/api/, re-stamp to the APW-07 block).down()drops only the two tables. Test:apps/api/src/migrations/__tests__/CreateAppEnvAndDependencies.spec.ts— both tables, FKsON DELETE CASCADE, indexes; noALTER TABLE "works"; the partial unique index is the Postgres-guarded raw form while SQLite and MySQL/MariaDB get their own branches (APW07-G12); up/down on the in-memory SQLite lane plus the opt-in Postgres run (EVER_WORKS_POSTGRES_RACE_TEST_URL,describe.skipwhen unset — the convention ofapps/api/src/works/existing-website-link.postgres.integration.spec.ts:12-13). Done when:pnpm --filter ever-works-api test -- CreateAppEnvAndDependenciesis green on SQLite, the opt-in Postgres run is green when the URL is set, and the timestamp is above the newestdevelopmigration. -
T8. Repositories. Create
packages/agent/src/database/repositories/work-app-env-value.repository.ts(findByWork,insertIfAbsent→ returns the stored row,upsertValuewith version + 1,deleteNames,totals(workId)→{ count, bytes }) andpackages/agent/src/database/repositories/work-app-dependency.repository.ts(findActiveByWork,findByWorkAndKind,claimLease(id, ms),markKept,markDeleted,updateOutputs(id, envelope)withoutputsVersion + 1). Modifypackages/agent/src/database/index.ts(exports) and, if the repository inventory requires it,packages/agent/src/database/_repository-inventory.ts(added, APW07-G24:database.module.spec.ts:41enforces that inventory, so a repository missing from it fails that spec — register both there rather than only in the feature module). Test:packages/agent/src/database/repositories/__tests__/work-app-env-value.repository.spec.ts— 20 concurrentinsertIfAbsent→ one row and all callers read the same envelope (ACC-07-02);packages/agent/src/database/repositories/__tests__/work-app-dependency.repository.spec.ts— lease exclusivity; a second active row for the same kind is refused while a kept one is allowed; the lease is a parameterised timestamp compare-and-set with nonow() + intervalSQL (APW07-G12). Done when:pnpm --filter @ever-works/agent test -- work-app-env-value.repository work-app-dependency.repositoryis green on the SQLite lane and on the opt-in Postgres run (EVER_WORKS_POSTGRES_RACE_TEST_URL).
P1.3 — Env core
-
T9.
AppEnvCrypto. Createpackages/agent/src/app-env/app-env-crypto.tswrappingpackages/agent/src/plugins/services/plugin-secret-enc.service.ts(plan §4.1). Test:packages/agent/src/app-env/__tests__/app-env-crypto.spec.ts— no key →AppEnvEncryptionUnavailableErrorunderNODE_ENVproduction,developmentandtest, and a spy repository records zero writes (ACC-07-12); round-trip with a key; unprefixed input refused. Done when:pnpm --filter @ever-works/agent test -- app-env-cryptois green. -
T10 (parallel with T9). Generators. Create
packages/agent/src/app-env/generators.ts(plan §4.3) using onlynode:cryptofor randomness (thepemkeypair format here; other formats in T42). Test:packages/agent/src/app-env/__tests__/generators.spec.ts— 1,000 samples each: base64 of 24 bytes → 32 chars, hex of 32 → 64, chars 40 alnum → 40 with membership, uuid → 36 and version nibble4(ACC-07-01);charsbyte-frequency chi-square below the 99.9% critical value for 62 buckets; each keypair type produces PKCS#8/SPKI PEM whose public key verifies a signature made with the private key (ed25519, ec-p256, rsa-2048; rsa-4096 once) (ACC-07-06); fingerprints stable. Done when:pnpm --filter @ever-works/agent test -- generatorsis green in under 60 seconds. -
T11 (parallel with T9). Validation. Create
packages/agent/src/app-env/validation.ts(plan §4.4); Modifypackages/agent/package.json— addre2js. Test:packages/agent/src/app-env/__tests__/validation.spec.ts— every refusal code; a 44-character value forlength: 32→lengthMismatch { expected: 32, actual: 44 }with the S15 message;(a+)+$against 65,536 ×a+!completes under 50 ms (ACC-07-07);EVER_WORKS_FOO→reservedName,bad-name→invalidName, a 65,537-byte value →valueTooLarge(ACC-07-08); code-point counting ("é" × 32passeslength: 32); a look-around pattern is refused as unsupported; no message contains the tested value. Done when:pnpm --filter @ever-works/agent test -- validationis green. -
T12 (parallel with T9). Dotenv parser. Create
packages/agent/src/app-env/dotenv-parser.ts(plan §4.5) and fixturepackages/agent/src/app-env/__tests__/fixtures/import-12-lines.env. Test:packages/agent/src/app-env/__tests__/dotenv-parser.spec.ts— the fixture yields 11 entries + 1 refusal at line 7 (ACC-07-11);exportprefix; single quotes literal; double-quote escapes and multi-line; inline#comment; BOM; CRLF; duplicates last-wins; 501 lines and 65,537 bytes refused before parsing; a spy proves no logger orconsolecall. Done when:pnpm --filter @ever-works/agent test -- dotenv-parseris green. -
T13.
AppEnvService. Createpackages/agent/src/app-env/app-env.service.ts,packages/agent/src/app-env/app-env.module.ts,packages/agent/src/app-env/index.ts(plan §4.2) —list,ensureGenerated,apply(set / unset / reset / import with per-item results),rotate,missingRequired,buildRedactor. Test:packages/agent/src/app-env/__tests__/app-env.service.spec.ts— S1 fixture counts (6 generated set, 2 required prompted missing); callingensureGeneratedagain after a re-apply of the same App spec, and after a simulated rebuild, redeploy and upstream-sync event, leaves every generatedversionand envelope unchanged (ACC-07-03); the 12-line import stores 9 declared, creates 2 undeclared with the warning, refuses line 7 and skips a generated name unlessreplaceGenerated+acknowledgeNeverRotate(ACC-07-11);missingRequired('runtime')lists both names with descriptions (ACC-07-09); set on afromentry becomes an override andresetrestores; keypair and<NAME>_PUBLICcannot be set;generatorChangedafter the fixture'sbyteschanges, value unchanged; 301st value and 1 MiB + 1 byte refused;EVER_WORKS_Xrefused; oneapp.env.changedper call with names and actions only; the redactor replaces every stored value ≥ 6 chars with***. Done when:pnpm --filter @ever-works/agent test -- app-env.serviceis green. -
T14.
AppEnvResolverand theAppRuntimeEnvSourceport. Status 2026-09-26: Status notes. Createpackages/agent/src/app-env/app-env.resolver.ts(plan §2.2 and §4.6) andpackages/agent/src/app-env/app-env-runtime.source.tsimplementing APW-06'sAppRuntimeEnvSource(packages/agent/src/app-runtime/ports.ts, typed copy until APW-06 lands) bound toAPP_RUNTIME_ENV_SOURCE, withew-dep://<kind>/<output>placeholders decided fromctx.target(never the stored row) andresolveEphemeral(T43). Added by the 2026-09-17 fix pass:resolvereturnsfingerprints(the §2.2 per-name map, same keys asvalues), readsctx.target, derivesnotReadyDependenciesfromAppDependenciesService.ensureReadyForDeploy(workId)(which dispatchespendingkinds — GAP-05), andresolveEphemeral'sclustertarget readsctx.dependencyOutputsfor derived references (APW07-G03, APW07-G04). Test:packages/agent/src/app-env/__tests__/app-env.resolver.spec.ts— one case per §2.2 row for both phases; onlybuild/bothentries reachresolveForBuildand a build-phasedeps.postgres.urlresolves topostgresql://ever-works-build:…@127.0.0.1:5432/app(ACC-07-10); a derived build-phase entry's fingerprint differs after the primary domain changes, sochangedSinceBuildflips (ACC-07-13);platform.smtp.*at build →notAvailableAtBuild; dependency not ready → listed innotReadyDependenciesand exactly oneensureReadyForDeploycall; a non-requiredsmtpwith no provider leaves its entries unset with thesmtpNotConfiguredwarning instead of blocking (ACC-07-33, FR-62); template depth 11 →templateUnresolvable; fingerprints follow the §2.2 rule, includingt<…>for a secret template — its fingerprint changes when an input fingerprint changes and no sha256 of a secret value is ever produced;fingerprintshas a key for every key invalues; unresolved items never carry a value.packages/agent/src/app-env/__tests__/app-env-runtime.source.spec.ts— port shape (values,fingerprints,secretNames,unsetRequirednaming both unset required values — ACC-07-09,notReadyDependencies,egresshost/ports forsmtp-externalands3-external); keypair<NAME>_PUBLICincluded and the private value only invalues(ACC-07-06); managed target emits placeholders and no output value;clusterephemeral mode resolves a derived reference fromctx.dependencyOutputsand stores nothing (ACC-07-31). Done when:pnpm --filter @ever-works/agent test -- app-env.resolver app-env-runtime.sourceis green. -
T15. Listener. Status 2026-09-26: Status notes. Create
packages/agent/src/app-env/app-env.listener.ts—app.spec.applied→ensureGeneratedthenAppDependenciesService.reconcile(T16). Test:packages/agent/src/app-env/__tests__/app-env.listener.spec.ts— generated rows exist when the handler call returns, well inside 60 s (ACC-07-01); a thrown reconcile does not undo generation. Done when:pnpm --filter @ever-works/agent test -- app-env.listeneris green.
P1.4 — Dependencies core and providers
-
T16.
AppDependencyFacadeServiceandAppDependenciesService. Createpackages/agent/src/facades/app-dependency.facade.ts(extendsBaseFacadeService,CAPABILITY = PLUGIN_CAPABILITIES.APP_DEPENDENCY, preference-ordered selection) andpackages/agent/src/app-dependencies/app-dependencies.service.ts,app-dependencies.module.ts,index.ts(reconcile,ensureReadyForDeploy,onAppRemoved,list,configure,retry,requestDataDeletion). Modifypackages/agent/src/facades/facades.module.ts,packages/agent/src/facades/index.ts. Test:packages/agent/src/app-dependencies/__tests__/app-dependencies.service.spec.ts— reconcile transitions (new kind →pending+ dispatch, orawaiting_config+ no dispatch and no deadline for a provider withawaitingConfig: true; removed kind →inSpec=false, no dispatch; target change → oldkept, new pending);onAppRemoved({ deleteData: false }), removal from the spec and a target change each leave the provider'sdeprovisionuncalled or called withdeleteData: false(ACC-07-21); targetnone→ nothing provisioned; explicit provider choice wins when supported;ensureReadyForDeploylists not-ready kinds; a facade-source grep finds no provider id literal; a Work with a declared Postgres and no Deployment callsAppRuntimeTargetPort.prepareDependencyTargetonce and reachesreadywith zeroapp-deploydispatches (the GAP-06 / APW07-G01 deadlock regression),target_not_checkedandnamespace_owned_elsewheregive failed with that reason, andcluster_unreachableis retried 3 times (ACC-07-14). Done when:pnpm --filter @ever-works/agent test -- app-dependencies.serviceis green. -
T17.
app-dependency-provisiondispatcher and job. Createpackages/agent/src/tasks/app-dependency-provision-dispatcher.ts,app-dependency-provision.types.ts,packages/agent/src/app-dependencies/app-dependency-provision.runner.ts(plan §7),packages/tasks/src/tasks/trigger/app-dependency-provision.task.tson APW-06's queueapp-cluster-io(refuses to dispatch in production unlessEVER_WORKS_APPS_CLUSTER_WORKER_ISOLATED=true). Modifypackages/agent/src/tasks/index.ts,packages/agent/src/tasks/_tasks-symbols.ts(APP_DEPENDENCY_PROVISION_DISPATCHER),packages/agent/src/tasks/job-runtime.providers.ts,packages/tasks/src/trigger/trigger.service.tsandpackages/tasks/src/trigger/trigger.module.ts(added, APW07-G24: each dispatcher needs a concreteTriggerService.dispatchAppDependencyProvision(payload)method that propagates errors, plus its mirror indispatchersFromTenantClientwith the propagate shape rather thansoftDispatch, and itsTASK_IDSentry; the arity pin inpackages/agent/src/tasks/__tests__/job-runtime.providers.spec.tsis updated by counting the mergedDISPATCHER_SYMBOLSlist). The delayed re-dispatch uses the existingnotBefore→deferUntil→delayshape (packages/tasks/src/trigger/trigger.service.ts:581-591), never a sleep in the job. Test:packages/agent/src/app-dependencies/__tests__/app-dependency-provision.runner.spec.ts— lease;pendingre-dispatch ≤ 30 s until the kind's deadline thenfailed deadlineExceeded; a cluster-unreachable outcome retried 3 times at 5-minute spacing beforefailed clusterUnreachable(ACC-07-20); definite failure immediate; outputs encrypted andoutputsVersion + 1only when changed;refreshupdates backup state;deprovisionkeep →app.dependency.releasedand rowkept(ACC-07-21), delete →app.dependency.data_deletedand rowdeleted(ACC-07-22);packages/agent/src/tasks/tasks.spec.tspasses. Done when:pnpm --filter @ever-works/agent test -- app-dependency-provision.runner tasks.specis green. -
T18.
KubernetesApiServicehelpers. Modifypackages/plugins/k8s/src/k8s-api.service.ts— add onlycrdServed(name, version)anddefaultStorageClass(); reuse APW-06'sapplyObject,readObject,listObjects,deleteObject,createSelfSubjectAccessReview(if APW-06 has not landed, add those five with APW-06's exact signatures from its plan §6.3 and let APW-06 adopt them). Test:packages/plugins/k8s/src/__tests__/k8s-api.dependencies.spec.tsagainst the existing client factory mock — 404 CRD →false; served-version mismatch →false; aStorageClassannotated default is returned and none →null(ACC-07-20). Done when:pnpm --filter @ever-works/k8s-plugin test -- k8s-api.dependenciesis green and every pre-existing k8s plugin spec passes unchanged. -
T19.
k8s-inline-postgres. Createpackages/plugins/k8s/src/app-dependencies/common.ts(labels, security context, network policy),packages/plugins/k8s/src/app-dependencies/images.ts(digest-pinned images per Postgres version 14–17, Redis 7, S3 server, client job images),packages/plugins/k8s/src/app-dependencies/postgres.provider.ts(operator path + plain path, extensions, outputs, backup state, deprovision incl.stopWorkloads, ephemeral variant). Modifypackages/plugins/k8s/src/k8s.plugin.ts—capabilities: ['deployment', 'app-dependency'],dependencyProviders, delegatesupports/provision/getOutputs/deprovision/backupStatus; settingsappDependencyStorageClass,appDependencySizes, admin image overrides. Modifypackages/plugins/k8s/package.json— capabilities array. Test:packages/plugins/k8s/src/app-dependencies/__tests__/postgres-operator-path.spec.ts— CRD served + allowed →Clustermanifest withinstances: 1(ACC-07-15); CRD served + denied → plain path withoperatorSkipped; backup states from Backup objects, the newestcompleted→ healthy,failed→ failing, none → not configured, including a cluster whose summary claims success while the newest Backup failed →failing(ACC-07-15); no Backup + ScheduledBackup 27 h old →overdue.packages/plugins/k8s/src/app-dependencies/__tests__/postgres-plain-path.spec.ts— StatefulSet for Postgres 16 with a readiness probe and a 10 GiB claim, backup statenone(the no-backup warning); security context uid 999,runAsNonRoot, drop ALL; APW-06 labels plusever-works.io/dependencyandever-works.io/retainon the PVC; the provider applies NetworkPolicydep-postgres(same-namespace ingress only, plus the operator namespace on the operator path) before the StatefulSet orCluster, so a pod outside the namespace cannot connect (ACC-07-14) and the policy is drawn with isolation off too (APW07-G01); no default storage class →failed noDefaultStorageClasswith the S18 reason (ACC-07-20); a 403 on StatefulSet create →clusterPermissionMissing(APW07-G10);directUrloutput only when declared. Done when:pnpm --filter @ever-works/k8s-plugin test -- postgres-operator-path postgres-plain-pathis green. -
T20 (parallel with T19).
k8s-inline-redis. Createpackages/plugins/k8s/src/app-dependencies/redis.provider.ts. Test:packages/plugins/k8s/src/app-dependencies/__tests__/redis.spec.ts— Deployment without persistence, StatefulSet + 1 GiB PVC with;--maxmemory 400mband the declared policy; readiness usesREDISCLI_AUTH(no password in args); ready whenreadyReplicas == 1inside the 5-minute deadline (ACC-07-16); outputs URL shape;dep-redisadmits same-namespace ingress only and is applied before the workload (APW07-G01). Done when:pnpm --filter @ever-works/k8s-plugin test -- redisis green. -
T21 (parallel with T19).
k8s-inline-minio. Createpackages/plugins/k8s/src/app-dependencies/object-storage.provider.ts. Test:packages/plugins/k8s/src/app-dependencies/__tests__/object-storage.spec.ts— StatefulSet 20 GiB; init Job creates every declared bucket and ready requires Job success inside 10 minutes (ACC-07-16); anonymous download only onpublicBuckets; service-account keys written todep-s3-app; outputs;deleteData: falsemakes zero API calls andtruedeletes PVCs and re-lists to zero;dep-s3admits same-namespace ingress only and is applied before the init Job and the StatefulSet (APW07-G01). Also (T20/T21, APW07-G01). The shareddep-<kind>rendering lives incommon.ts, so its unit spec covers the label set,podSelector, the three ingress ports, the operator-namespace variant and the fact that it is drawn with isolation off. Done when:pnpm --filter @ever-works/k8s-plugin test -- object-storageis green. -
T22.
app-dependencies-externalplugin (SMTP, S3). Createpackages/plugins/app-dependencies-external/(package.jsonnamed@ever-works/app-dependencies-external-pluginwitheverworks.pluginidapp-dependencies-external, categoryapp-dependency, capabilityapp-dependency, and the manifest flags thek8smanifest carries —autoEnable,builtInandvisibility(added, APW07-G24:BaseFacadeServiceselects only plugins enabled for the user, so a plugin without them leaves SMTP and S3 permanently unavailable); depsnodemailer,@aws-sdk/client-s3; tsup/vitest likepackages/plugins/k8s),src/index.ts,src/plugin.ts,src/smtp-external.provider.ts,src/s3-external.provider.ts,src/public-endpoint.ts(DNS-resolved private, loopback, link-local refusal honouringEVER_WORKS_APP_DEPENDENCY_PRIVATE_ALLOWLIST— added, APW07-G09). Test:packages/plugins/app-dependencies-external/src/__tests__/smtp-external.spec.ts— verify success; connect/TLS failures → reasons; an auth refusal →smtpAuthRefusedwithin the 30 s abort andsendMailnever called (ACC-07-17).s3-external.spec.ts—HeadBucketper mapping; a missing bucket →bucketUnreadablenaming it (ACC-07-18).public-endpoint.spec.ts—127.0.0.1,10.x,169.254.x,::1, and a hostname resolving to a private address are refused, while an allow-listed CIDR is accepted and a non-listed one is still refused (APW07-G09, ACC-07-33's lane). Done when:pnpm --filter @ever-works/app-dependencies-external-plugin testis green and prompt schemas markpasswordandsecretAccessKeyx-secret. -
T23.
platform-smtp-relayprovider. Createpackages/plugins/app-dependencies-external/src/platform-smtp-relay.provider.tsand admin settings (relay.apiUrl,relay.apiTokenx-secret,relay.host,relay.port,relay.fromDomain,relay.dailyLimitdefault 200). Test:packages/plugins/app-dependencies-external/src/__tests__/platform-smtp-relay.spec.ts— the descriptor is absent (not disabled) unless all five required settings are set (ACC-07-19); provision posts{ id: "work-<uuid>", dailyLimit: 200 }; deprovision deletes that id; outputs never contain the operator'sapiToken. Done when:pnpm --filter @ever-works/app-dependencies-external-plugin test -- platform-smtp-relayis green.
P1.5 — API and events
-
T24. App env controller. Create
apps/api/src/app-env/app-env.controller.ts,apps/api/src/app-env/dto/app-env.dto.ts(ApplyAppEnvDto,RotateAppEnvDto),apps/api/src/app-env/app-env.module.ts; routes, throttles and codes from plan §5, accessed throughAppWorkAccessService.resolve(APW07-G13) and marked@SensitiveRequestBody()(APW07-G05). Modifyapps/api/src/api.module.tsto import the module; createpackages/monitoring/src/decorators/sensitive-request-body.decorator.ts(new) and modifypackages/monitoring/src/interceptors/sentry.interceptor.tsto record{ redacted: true }for a marked route instead of the body. Test:apps/api/src/app-env/app-env.controller.spec.ts— foreign id 404 on every route with the realAppWorkAccessServiceover stubbed repositories, and a viewer 403 on PUT and rotate (ACC-07-23, APW07-G13); 503secureStorageUnavailablewithout key (ACC-07-12); rotate without or with a wrongconfirmName422, with it 200 and the entry's version + 1 (ACC-07-04); 11th rotation 429; every response body and all captured logger output scanned for each submitted value → absent (ACC-07-05); the error-reporting interceptor's captured context calls contain no submitted value and noset.*,import.dotenvorconfig.*fragment (APW07-G05); import results per line (ACC-07-11).packages/monitoring/src/interceptors/__tests__/sentry.interceptor.spec.ts— a marked route yields{ redacted: true }while an unmarked route is unchanged. Done when:pnpm --filter ever-works-api test -- app-env.controlleris green. -
T25. App dependencies controller. Create
apps/api/src/app-dependencies/app-dependencies.controller.ts,apps/api/src/app-dependencies/dto/app-dependencies.dto.ts,apps/api/src/app-dependencies/app-dependencies.module.ts; Modifyapps/api/src/api.module.ts. Access throughAppWorkAccessService.resolve(APW07-G13);@SensitiveRequestBody()on every write (APW07-G05);PUT …/:kindwithsizeGiBvalidates the size (below the provisioned size → 422sizeShrinkRefused; a class that cannot expand → 422volumeExpansionUnsupported; otherwise aresizedispatch) — APW07-G22. Test:apps/api/src/app-dependencies/app-dependencies.controller.spec.ts— GET never contains config or output values (seeded envelopes decrypted in the test and searched for) (ACC-07-05); stalelastCheckedAtdispatches one refresh; PUT unknown provider 422; DELETE wrong slug 422 and right slug 202 + onedeprovisiondispatch withdeleteData: true(ACC-07-22); undeclared kind 404; foreign id 404 on every route (ACC-07-23); anawaiting_configcard dispatches nothing and a valid PUT moves it topendingwith one dispatch (ACC-07-33); a smallersizeGiB→ 422sizeShrinkRefused, a larger one on a non-expandable class → 422volumeExpansionUnsupported, a larger one on an expandable class → 202 and oneresizedispatch (ACC-07-34). Done when:pnpm --filter ever-works-api test -- app-dependencies.controlleris green. -
T26 (parallel with T25). Activity types. Modify
packages/agent/src/entities/activity-log.types.ts—APP_ENV = 'app_env',APP_DEPENDENCY = 'app_dependency'. Createpackages/agent/src/app-env/app-env.activity.tsandpackages/agent/src/app-dependencies/app-dependency.activity.tswritingapp.env.changed|rotatedandapp.dependency.provisioned|failed|released|data_deletedviaActivityLogService.logwithaction= the dotted name (R-2). Test:packages/agent/src/app-env/__tests__/app-env.activity.spec.ts—app.env.rotatedcarries the name only (ACC-07-04); metadata holds names/actions/kinds only and a property test with random values never finds a value in the serialised row (ACC-07-05);actionTypeisapp_env/app_dependency. Done when:pnpm --filter @ever-works/agent test -- app-env.activityis green.
P1.6 — Web
-
T27. Routes, sub-tabs, client and actions. Create
apps/web/src/app/[locale]/(dashboard)/works/[id]/settings/environment/page.tsx,apps/web/src/app/[locale]/(dashboard)/works/[id]/settings/dependencies/page.tsx,apps/web/src/lib/api/app-env.ts,apps/web/src/app/actions/dashboard/app-env.ts(seven actions in plan §6). Modifyapps/web/src/components/works/detail/settings/SettingsSubTabs.tsx— Environment and Dependencies after General whenappEnvironmentis true. Test:apps/web/src/components/works/detail/settings/SettingsSubTabs.unit.spec.tsx(create if absent) — tabs hidden fordirectory, shown when the capability is true. Done when:pnpm --filter ever-works-web test -- SettingsSubTabsis green and a directory Work's settings are unchanged. -
T28. Environment table and dialogs. Create
apps/web/src/components/works/detail/settings/app-env/AppEnvTable.tsx,AppEnvSetDialog.tsx,AppEnvRotateDialog.tsx,AppEnvImportDialog.tsx,AppEnvAddDialog.tsx. Test:apps/web/src/components/works/detail/settings/app-env/AppEnvTable.unit.spec.tsx— origin/phase/state chips for each §6.1 row incl. Changed since the last build (ACC-07-13); no reveal control; Copy public key only on keypair rows and copies the public half (ACC-07-06).AppEnvSetDialog.unit.spec.tsx— field empty on every open; cleared on close, save and unmount; hold-to-show disabled after save.AppEnvRotateDialog.unit.spec.tsx— button disabled until the exact name is typed (ACC-07-04).AppEnvImportDialog.unit.spec.tsx— per-line results; replace-generated needs the second confirmation. Done when:pnpm --filter ever-works-web test -- AppEnvTable AppEnvSetDialog AppEnvRotateDialog AppEnvImportDialogis green. -
T29 (parallel with T28). Dependency cards, dialogs and the deploy notice. Create
apps/web/src/components/works/detail/settings/app-dependencies/AppDependencyCard.tsx,AppDependencyConfigureDialog.tsx,AppDependencyDeleteDataDialog.tsx, andapps/web/src/components/works/detail/settings/app-env/DeployBlockedByEnvNotice.tsx(exported for APW-06). Test:apps/web/src/components/works/detail/settings/app-dependencies/AppDependencyCard.unit.spec.tsx— every status and backup state renders its copy; the no-backup warning is a warning role (ACC-07-14); 10 s polling starts/stops; a viewer sees Configure, Retry and Delete data disabled with "You need edit access to do this." (ACC-07-23).AppDependencyDeleteDataDialog.unit.spec.tsx— lists kept resources; enabled only on the exact slug (ACC-07-22).AppDependencyConfigureDialog.unit.spec.tsx— secret inputs never pre-filled. Done when:pnpm --filter ever-works-web test -- AppDependencyCard AppDependencyDeleteDataDialog AppDependencyConfigureDialogis green.
P1.7 — i18n, tests, docs
-
T30. i18n. Modify
apps/web/messages/en.jsonwith every key in plan §8; mirror into the 20 sibling files underapps/web/messages/. Test:node apps/web/scripts/sync-locale-parity.mjs && git diff --exit-code apps/web/messagesadds zero keys (ACC-07-25); a grep over the new leaves finds no.. Done when: both commands exit 0 in the PR. -
T31. E2E. Create
apps/web/e2e/app-env-table.spec.ts(origins, set → value never re-rendered, rotate typed name — ACC-07-04, import results — ACC-07-11),apps/web/e2e/app-env-deploy-blocked.spec.ts(Deploy refused listing both unset names with descriptions — ACC-07-09),apps/web/e2e/app-dependencies-cards.spec.ts(seeded states incl. the no-backup warning — ACC-07-14, failed reason, delete-data dialog),apps/web/e2e/app-env-a11y.spec.ts(axe on both pages and all dialogs with no new violations + keyboard — ACC-07-25) (plan §10.3), seeded via the API withEVER_WORKS_E2E_FAKES; every spec scans captured network responses and the page for each seeded value (ACC-07-05). Added by the 2026-09-17 fix pass. Modify.github/workflows/e2e.ymlto set a test-onlyPLUGIN_SECRET_ENCRYPTION_KEY(APW07-G06: App env refuses every write and generation without it in everyNODE_ENV, and that workflow currently sets onlyPLATFORM_ENCRYPTION_KEY, so no row could ever readSet) and add a harness preflight that fails fast withsecureStorageUnavailablewhen the key is missing; ask APW-13's T34 for the same variable inapp-works-kind.yml, and document the requirement inapps/api/.env.exampleand T33's docs page. Create the non-production fake dependency provider of T46 and drive the card states through it (APW07-G07:EVER_WORKS_E2E_FAKESonly points Git at the fake GitHub and the PR lane has no cluster, so "seeded states incl. the no-backup warning, failed reason, delete-data dialog" had no way to exist). Test:pnpm --filter ever-works-web test:e2e app-env- app-dependencies-cards. Done when: all four pass locally and ine2e.yml, and the existing deploy runtime-env e2e specs pass unchanged. -
T32. Live acceptance wiring. Modify
docs/specs/features/app-works/ACCEPTANCE.md(the APW-07 table in §3, through its owner) — map ACC-07-01…25, 30 and 31 to the APW-13 harness scenarios on the two kind clusters (with and without the operator), the local SMTP server and the S3-compatible test server. Test:rg -n "ACC-07-(0[1-9]|1[0-9]|2[0-5]|30|31)" docs/specs/features/app-works/ACCEPTANCE.mdlists every id with a scenario name. Done when: every P1 ACC-07 id names its scenario. -
T33. Docs. Create
docs/features/app-env-and-dependencies.md— origins, generators and exact lengths, keypair formats, never-rotate,.envimport grammar, providers per target, backup states, deleting data, deleting an App Work. Modifyapps/docs/sidebarsPlatform.ts,docs/plugin-system/built-in-plugins.md(addapp-dependencies-external; note the k8s plugin's new capability),docs/plugin-system/plugin-categories.md(addapp-dependency). Test:pnpm --filter ever-works-docs buildandgit diff --exit-code docs/runbooks/WORK_RUNTIME_ENV.md. Done when: the build reports no broken links and the runbook has no diff. -
T34. P1 ship gate. Modify
docs/specs/features/app-works/TRACKER.md— tick APW-07 P1. Test: rootpnpm format:check,pnpm lint,pnpm type-check,pnpm test,pnpm build;pnpm --filter @ever-works/agent test -- work-runtime-env.servicepasses withpackages/agent/src/services/work-runtime-env.service.spec.tsunchanged (ACC-07-24); ACC-07-01…25, 29, 30 and 31 walked. Done when: every command exits 0,git diff --exit-code packages/agent/src/services/work-runtime-env.service.spec.tsexits 0 and each walked criterion is recorded.
Phase P2 — Dependencies on Ever Works Apps
Delivers spec FR-51…FR-55 and ACC-07-26…28. Starts only after APW-10's launch gate passes. Managed dependencies are
resolved in the zone (APW-10); the platform never holds tenant data-server credentials and asks only
AppsTierPolicy whether the tier is open (R-5).
-
T35. Tenant Postgres DDL builder and platform-server refusal. Create
packages/contracts/src/apps/tenant-postgres-ddl.ts— pure, dependency-freebuildTenantPostgresDdlandisPlatformDataServer(url, platformEndpoints)(plan §4.11) so APW-10's zone controller can import them. Test:packages/contracts/src/apps/__tests__/tenant-postgres-ddl.spec.ts— exact statement order; every identifier double-quoted;CONNECTION LIMIT 20on the role and25on the database;REVOKE CONNECT, TEMPORARY … FROM PUBLIC;statement_timeout = '60s'andidle_in_transaction_session_timeout = '60s'(ACC-07-26); re-running yieldsALTERnotCREATE;isPlatformDataServer— host case, IPv6 brackets, default port 5432 equivalence, and a configured platform server is refused (ACC-07-27). Done when:pnpm --filter @ever-works/contracts test -- tenant-postgres-ddlis green andpackages/agent/src/ever-works-providers/ever-works-db-provision.service.tshas no diff. -
T36.
apps-tier-dependenciesplugin. Createpackages/plugins/apps-tier-dependencies/(package@ever-works/apps-tier-dependencies-plugin;src/index.ts,src/plugin.ts,src/managed-postgres.provider.ts,src/managed-redis.provider.ts,src/managed-object-storage.provider.ts,src/managed-smtp.provider.tsfor providersmanaged-postgres,managed-redis,managed-object-storage,managed-smtp— GAP-22;provisionwrites{ kind, ref }throughIAppsTierProvider.setDependencies(workId, deps)and readsWork.status.dependencies[]; no endpoint or credential settings;outputsEncryptednever written;releaseDependencies(workId, { deleteData })on removal). The change wasapplyWorkbefore, which replaces the whole desired state and would have restarted the app's workloads for a dependency change (APW10-G01 / APW07-G19). Modifypackages/agent/src/facades/app-dependency.facade.ts— resolvable only whenAppsTierPolicy.isOpen()and the target isever-works-apps(R-5). Test:packages/plugins/apps-tier-dependencies/src/__tests__/apps-tier-dependencies.plugin.spec.tsagainst anIAppsTierProviderfake — refs written per declared kind throughsetDependencies, neverapplyWork; ready mirrors zone status; no connection string or role name leaves the zone fake (ACC-07-26);managed-smtpis offered onever-works-appsand its outputs carry the relay endpoint, credential and from-address, so asmtp: { required: true }App Work reachesreadywhile the tier's ports 25/465/587 stay refused (ACC-07-32, FR-61). Extendpackages/agent/src/app-dependencies/__tests__/app-dependencies.service.spec.ts— the facade never offers these providers for Your cluster or while the policy is closed, and a spy provesEVER_WORKS_APPS_MANAGED_ENABLEDis never read. The live isolation checks (another App Work's role refused, 21st connection refused, a 70-second statement cancelled at 60 s, bucket policy prefix, 10 GiB quota) run in APW-10's gated environment as ACC-07-26 via probe LG-14, extended withCONNECTION_LIMIT_NOT_ENFORCEDandSTATEMENT_TIMEOUT_NOT_ENFORCED(APW10-G01). Done when:pnpm --filter @ever-works/apps-tier-dependencies-plugin testandpnpm --filter @ever-works/agent test -- app-dependencies.serviceare green. -
T37. Managed backup reporting. Modify
packages/plugins/apps-tier-dependencies/src/managed-postgres.provider.ts,managed-redis.provider.ts,managed-object-storage.provider.ts—backupStatusreadsWork.status.dependencies[].lastBackupAt;overduebeyond 24 hours. Test: extendpackages/plugins/apps-tier-dependencies/src/__tests__/apps-tier-dependencies.plugin.spec.ts— fresh, 27-hour-old (the FR-48 overdue line) and absent timestamps map tohealthy,overdue,unknown, and a card view built from a 3-hour-old timestamp reads "last completed" (ACC-07-28). APW07-G25: the card's overdue line is FR-48's 26 hours (APP_DEPENDENCY_BACKUP_OVERDUE_MS) for every card;APP_DEPENDENCY_MANAGED.backupMaxAgeMs(24 h) is the zone's own schedule target, so a 25-hour-old timestamp readsoverdueonly because the zone missed that target, and the test asserts the 26/27-hour boundary rather than treating 24 h as the card's rule. Done when:pnpm --filter @ever-works/apps-tier-dependencies-plugin testis green. -
T38. P2 acceptance and ship gate. Modify
docs/specs/features/app-works/ACCEPTANCE.md(APW-07 rows ACC-07-26…28, through its owner) anddocs/specs/features/app-works/TRACKER.md(tick P2). Test: ACC-07-26…28 walked in APW-10's gated environment (evidence in the private operations repository); rootpnpm format:check,pnpm lint,pnpm type-check,pnpm test,pnpm build. Done when: the three criteria are recorded green and every root command exits 0.
Cross-phase closing tasks
-
T39. Telemetry. Create
packages/agent/src/app-env/app-env.telemetry.tsandpackages/agent/src/app-dependencies/app-dependencies.telemetry.tsemitting the events in plan §9.1; Modifypackages/agent/src/app-env/app-env.service.tsandpackages/agent/src/app-dependencies/app-dependency-provision.runner.tsto call them. Test:packages/agent/src/app-env/__tests__/app-env.telemetry.spec.ts— no payload contains an env name, a value, a host, a bucket name or a namespace (ACC-07-05). Done when:pnpm --filter @ever-works/agent test -- app-env.telemetryis green. -
T40. Contracts and schema parity. Create
packages/agent/src/works-config/schema/__tests__/dependency-outputs-parity.spec.ts(new — moved out ofpackages/contracts/by APW07-G20:@ever-works/contractshas zero dependencies and must not import the agent package, so the parity spec cannot live there) assertingAPP_DEPENDENCY_OUTPUTSequals the outputs table APW-03 ships in its JSON Schema (packages/agent/src/works-config/schema/once APW-03 lands) andAPP_ENV_KEYPAIR_FORMATSequals the schema'skeypair.formatenum. APW-03's reference resolver and JSON Schema import those two constants from@ever-works/contractsinstead of re-declaring their own tables inapp-spec.refs.ts. Modifydocs/specs/features/app-works/TRACKER.mdfor APW-07 and, through its owner,CONTRACTS.mdif the merged code differs from this epic's rows (three dependency routes, two events,app-dependencycategory, provider id pair, ephemeral mode targets,stopWorkloads). Test:pnpm --filter @ever-works/contracts test -- dependency-outputs-parity. Done when: the parity spec is green and every CONTRACTS row this epic owns matches merged code. -
T41. Update statuses. Modify
docs/specs/features/app-works/APW-07-app-env-and-dependencies/spec.md,plan.mdand this file —Implemented/Done. Test: re-check every gate in plan §12 against the merged code with a reviewer. Done when: each gate is ticked with a link to the code or test that proves it, and known gaps remain listed.
Program audit follow-ups (added 2026-09-17)
-
T42. Keypair formats (Resolution R-11). Modify
packages/agent/src/app-env/generators.ts—keypair(type, format)per plan §4.3:base64url-rawfored25519/ec-p256via JWK export,pkcs12with a self-signed certificate encrypted with the value of thegenerate.keypair.passwordEnventry (generated first, stored as its own env value; rotating it re-packs the bundle); fingerprintkeypair:<type>:<format>[:<passwordEnv>]; format orpasswordEnvchange →generatorChanged. Modifypackages/agent/package.json— add@peculiar/x509andpkijs. Modifypackages/agent/src/app-env/app-env.service.ts— store the public half as<NAME>_PUBLIConly, for every format. Test:packages/agent/src/app-env/__tests__/keypair-formats.spec.ts—pemPKCS#8 + SPKI;ed25519base64url-rawprivate and public each 43 characters,ec-p256public 87 characters; an RSA type withbase64url-rawrefused;pkcs12opens with the password entry's value, fails with an empty passphrase, and its certificate's public key matches; the password entry is generated before the keypair and rotating it keeps the key pair; in every format a signature made with the private half verifies with the public half, and the only derived row is<NAME>_PUBLIC(ACC-07-29). Done when:pnpm --filter @ever-works/agent test -- keypair-formats generatorsis green. -
T43. Ephemeral mode and ephemeral dependencies (Resolution R-10). Modify
packages/agent/src/app-env/app-env-runtime.source.ts—resolveEphemeral(workId, specCommitSha, ctx)withctx.targetcluster(in-memoryvalues) andrunner(value-freerecipe), per plan §4.6.1. Modifypackages/agent/src/app-dependencies/app-dependencies.service.ts—provisionEphemeral(workId, namespace, kinds)calling providers withephemeral: trueand returning outputs in memory. Modifypackages/plugins/k8s/src/app-dependencies/postgres.provider.ts,redis.provider.ts,object-storage.provider.ts— the ephemeral variant (plain path,emptyDir, no stored outputs). Test:packages/agent/src/app-env/__tests__/app-env-ephemeral.spec.ts— a repository spy records zero inserts and updates onwork_app_env_valuesandwork_app_dependencies; two calls return different generated values; an unset required prompted value is listed inunsetRequiredby name; a set prompted value is included only forcluster; therunnerrecipe contains no value from the store or generator (sentinel search) (ACC-07-31); therunnerrecipe matches the normativeAppEnvRecipeEntryunion shape of plan §4.6.1 token by token, including the fixed container host/port/user grammar (postgres/redis/object-storage, neverminio) so APW-05 cannot build a different format (APW07-G18); aclustercall resolves a derived reference fromctx.dependencyOutputsand the values it produces never reach a repository (APW07-G04, ACC-07-31).packages/plugins/k8s/src/app-dependencies/__tests__/ephemeral.spec.ts— each provider renders noPersistentVolumeClaimorvolumeClaimTemplatesand returns outputs without calling any persistence callback (ACC-07-31). Done when:pnpm --filter @ever-works/agent test -- app-env-ephemeralandpnpm --filter @ever-works/k8s-plugin test -- ephemeralare green. -
T44. Deleting an App Work (Resolution R-15). Modify
packages/agent/src/app-dependencies/app-dependencies.service.ts—onAppWorkDeleting(workId, { deleteStoredData })per plan §4.12, idempotent. Modifypackages/plugins/k8s/src/app-dependencies/postgres.provider.ts,redis.provider.ts,object-storage.provider.ts—deprovisionoptionstopWorkloads(scale to 0 / hibernate; PVCs, Secrets and policies untouched). Test:packages/agent/src/app-dependencies/__tests__/app-dependencies.deletion.spec.ts— withoutdeleteStoredDatatwo ready dependencies are deprovisioned with{ deleteData: false, stopWorkloads: true }, both rows becomekept, and exactly twoapp.dependency.releasedand zeroapp.dependency.data_deletedevents are recorded; with it both are deleted and twoapp.dependency.data_deletedare recorded; apendingrow records nothing; a second call is a no-op (ACC-07-30).packages/plugins/k8s/src/app-dependencies/__tests__/deprovision.spec.ts—stopWorkloadsissues only scale patches and zero deletes (ACC-07-30). Done when:pnpm --filter @ever-works/agent test -- app-dependencies.deletionandpnpm --filter @ever-works/k8s-plugin test -- deprovisionare green. -
T45 (P1, lands with T5–T7). Classify new tables for workspace backup (R-25). Modify
packages/agent/src/account-transfer/backup/collectors/domain-specs.ts— append to theworksdomain:{ file: 'app-env-values.jsonl', entity: 'WorkAppEnvValue', scope: { by: 'parent', column: 'workId', from: 'workIds' } }and{ file: 'app-dependencies.jsonl', entity: 'WorkAppDependency', scope: { by: 'parent', column: 'workId', from: 'workIds' } }. Modifypackages/agent/src/account-transfer/backup/redaction.ts—ENTITY_SECRET_COLUMNSgainsWorkAppEnvValue: ['valueEncrypted']andWorkAppDependency: ['configEncrypted', 'outputsEncrypted'], so each becomes{ wasSet }while env names, origins, dependency kinds, statuses and backup states export as stored;ENTITY_DROPPED_COLUMNSgainsWorkAppEnvValue: ['valueBytes'](a value's length is never shown, plan §3.1). Neither table joinsBACKUP_DROPPED_ENTITIES. None of the three column names matches the shape rules or the secret-shaped guard inredaction.spec.ts, so these explicit entries are the only thing keeping the values out. Test: extendpackages/agent/src/account-transfer/backup/collectors/collectors.spec.ts— both entities are referenced exactly once, inworks, scopedparentonworkIdfromworkIds; anEntityBackupCollectorover theworksspec yields a fixtureWorkAppEnvValuerow{ name: 'SESSION_SECRET', valueEncrypted: 'enc::v1::<sentinel>', valueBytes: 44 }withnameintact,valueEncrypted: { wasSet: true }and novalueByteskey, andWorkAppDependencyrows whoseconfigEncrypted/outputsEncryptedbecome{ wasSet: true }(sentinel envelopes) or{ wasSet: false }(null, as forever-works-apps); no yielded line contains the sentinel orenc::v1::. Done when:pnpm --filter @ever-works/agent test -- collectors redactionis green, and removing either newENTITY_SECRET_COLUMNSentry turns the new case red.
Fix-pass additions (added 2026-09-17)
-
T46 (P1, lands with T22/T24/T25). Fake dependency provider, and the Activity-vs-telemetry boundary (APW07-G07, APW07-G14). Create
packages/plugins/app-dependencies-external/src/fake.provider.ts(new) — a non-productionapp-dependencyprovider registered only whenEVER_WORKS_E2E_FAKES=1andNODE_ENV !== 'production', whose outcomes (ready,failedwith a reason, any backup state,kept,awaiting_config) are driven through the existing fake-harness control endpoint. It is the only way the PR-lane Playwright specs can render the card states T31 asserts, because the PR lane has no cluster andEVER_WORKS_E2E_FAKEStoday only points Git at the fake GitHub. Createpackages/agent/src/activity-log/__tests__/app-activity-analytics.spec.ts(new) — pins the boundary plan §9.1 states: anapp_env/app_dependencyrow may carry names, kinds and counts to Activity and to the analytics sink (ActivityLogService.log→jitsu.service.ts, which forwardssummary,detailsand allmetadata), and the dispatched payload contains novalue,valueEncrypted,configEncrypted,outputsEncrypted, prompt, host, bucket name, connection string or namespace. Removing the guard turns it red. Test:pnpm --filter @ever-works/app-dependencies-external-plugin test -- fake.providerandpnpm --filter @ever-works/agent test -- app-activity-analyticsare green; the fake provider is absent in a production build. Done when: both specs pass andapp-dependencies-cards.spec.tsrenders every state T31 lists without a cluster. -
T47 (P1, lands with T19–T21). Dependency resize (APW07-G22 / FR-63). Modify
packages/agent/src/app-dependencies/app-dependency-provision.runner.ts— aresizemode that patches the volume claim when the storage class allows expansion, updatessizeGiB, emitsapp_dependency_resize(kind,fromGiB,toGiB) and records Activity with the two amounts; Modifypackages/plugins/k8s/src/app-dependencies/postgres.provider.ts,redis.provider.ts,object-storage.provider.ts— aresize(providerId, ctx, { sizeGiB })capability honouringallowVolumeExpansionon the storage class and reportingvolumeExpansionUnsupportednaming the class when it is off; Modifypackages/plugins/apps-tier-dependencies/src/managed-*.provider.ts— the managed variants resize through the zone's dependency record. Test:packages/plugins/k8s/src/app-dependencies/__tests__/resize.spec.ts(new) — a larger request patches the claim and leaves the workload untouched; a class withoutallowVolumeExpansion→volumeExpansionUnsupported; a smaller request never reaches the provider (it is refused at the API, T25). Extendapp-dependency-provision.runner.spec.ts— oneresizedispatch, one Activity row, quantities only. Done when:pnpm --filter @ever-works/k8s-plugin test -- resizeandpnpm --filter @ever-works/agent test -- app-dependency-provision.runnerare green and ACC-07-34 is walked. -
T48 (P1 test lane, cross-epic with APW-13). The live dependency lane needs an owner (APW07-G08). ACCEPTANCE §1 promises a PR-cluster lane with two kind clusters for APW-07 — one with the CloudNativePG operator, one without — plus an S3-compatible test server and a mail sink, but APW-13 sets up one kind cluster with no operator and no S3 server, and no spec file covers the live halves of ACC-07-14/15/16/20. Modify (through APW-13's owner)
.github/workflows/app-works-kind.yml— a matrixpostgresOperator: [none, cnpg]installing a pinned operator release on thecnpgleg, plus the S3-compatible test server and the mail sink; Createapps/web/e2e/flow-app-works-kind-dependencies.spec.ts(new) with one named scenario per ACC-07 id it proves (the operator path, the plain path, bucket creation, the no-storage-class failure, the 3×15 min retry); setEVER_WORKS_APP_DEPENDENCY_PRIVATE_ALLOWLISTfor the local sink (T22, APW07-G09). Test: the workflow's two legs are green, and T32'srgcheck names the new spec's scenarios rather than a generic clause. Done when:app-works-kind.ymlruns both legs and every live ACC-07 id names the scenario that proves it.
Definition of Done
- Every checkbox above is ticked for the phases being shipped.
pnpm format:check,pnpm lint,pnpm type-check,pnpm testandpnpm buildare green from the repo root.work-runtime-env.constants.ts,work-runtime-env.service.ts,ever-works-db-provision.service.ts,docs/runbooks/WORK_RUNTIME_ENV.mdandRuntimeEnvManagement.tsxhave no diff, and their specs pass unchanged.- A repository-wide scan of test output (API responses, logs, Activity rows, telemetry) for every value the suites set or generated returns zero matches.
- Every acceptance box in spec §8 for the shipped phase has been walked against real clusters and servers, and each ACC-07 id appears in at least one Test line above.
- No read of
EVER_WORKS_APPS_MANAGED_ENABLEDin this epic's code (R-5). - Every gate in plan §12 is confirmed, and its known gaps are still recorded there rather than silently closed.
Status notes
Dated status for the tasks above. It is kept here, not in the task bodies, so the task text keeps the line numbers that code comments and specs cite.
- T14 (2026-09-26): both halves now reach the API.
AppEnvModule(withAppEnvResolverandAPP_ENV_RESOLVER_FINGERPRINTS) is imported by APW-05'sAppBuildsModule(e23c2f844), andread(workId, 'build')resolves against the effective spec'sbuild.services, as the prepare runner does.AppRuntimeEnvModule(APP_RUNTIME_ENV_SOURCE) is imported by APW-06'sAppDeployRequestModule(3a956180e), which also makesAppBuildsModule's lazy runner-recipe lookup ofAppEnvRuntimeSourceresolve.APP_DEPENDENCY_SPEC_SOURCE(T25) is still unbound, soensureReadyForDeployanswersspecUnavailable. - T15 (2026-09-26,
3a956180e):AppEnvListeneris provided only byAppRuntimeEnvModule, which is now in the API graph throughAppDeployRequestModule's import, soapp.spec.appliedrunsensureGenerated(idempotent) andreconcilein the API. A class module is one instance however many modules import it, so the listener is subscribed once; do not provide it anywhere else.