Skip to content

Automate private Mailchimp account exports and attach them to recurring work #108

Closed as not planned

Description

@alexeygrigorev

Automate private Mailchimp account exports and attach them to recurring work

Status: blocked — source is accepted/deployed; waits for #166 steady state, external credential readiness, and one HUMAN real-provider acceptance run
Tags: enhancement, portal, frontend, backend, infra, data, P1
Depends on: #166 Phase D, canonical first-write evidence, and accepted cleanup; external Dapier credential owner
Satisfied source dependency: #120 is closed completed
Not a dependency: #187 changes one-off import-test selection only and preserves Mailchimp product tests
Blocks: None
Next owner: Dapier credential owner may record sanitized readiness; after #166 cleanup, an authorized DataOps mailing-export operator performs the HUMAN checklist
Resume condition: final canonical Tasks and ordinary deployment are restored, the Dapier item exists, and the non-secret mailing configuration targets the intended post-cutover recurring Task

Final source disposition

The agent-verifiable Mailchimp export product is complete.

Accepted commit e527164429e60c573cd14162a3e9020bd3184d16 is in current source and was deployed. Tester and PM accepted the provider-neutral request/poll/download flow, durable idempotency, private storage, Artifact/Task linkage, controlled download, portal behavior, safe failures, and privacy boundary. On-Call verified deployment and unauthenticated denial without calling Mailchimp.

#120 is now closed as the completed credential-source boundary. Its deployed reader uses the Dapier-owned credential_id: "mailchimp" record through exact-key, read-only IAM and no legacy Mailchimp Secrets Manager permission.

There is no source implementation, test, IAM, bucket, route, portal, or deployment work remaining under #108 unless the bounded HUMAN run reveals a new defect.

Product classification

This is a permanent source export product, not migration/import machinery.

It requests Mailchimp’s asynchronous account-wide audiences export, persists safe run state, stores the ZIP in retained private DataOps storage, creates one private Artifact, and attaches it to recurring work. That is retained under the user decision export from the data source — keep.

#174 removed migration-only APIs, planners, checkpoints, rollback/orphan frameworks, and ongoing importer tests. It explicitly retained live exports.

#187 keeps one-off dry-run import verification out of normal CI. It explicitly leaves Mailchimp, scheduled export, and archive tests in the ordinary backend suite because those tests protect shipped runtime behavior. #108 does not depend on #187 publication, and no one-off import/export/restore command is part of this HUMAN acceptance.

Do not delete, demote, or move the mailing-export product tests to one-off coverage. Do not add migration API support, generic credential access, or compatibility fallback.

Completed acceptance

  • Provider-neutral request, status polling, and download are separate from scheduling, persistence, storage, Task attachment, and presentation.
  • Mailchimp uses account exports with the audiences stage and honest account-wide semantics.
  • Runs have deterministic idempotency, durable pending/completed/failed state, safe retry actions, and no persisted provider download URL.
  • Repeated/concurrent delivery of one run key cannot create duplicate logical provider requests, objects, Artifacts, or Task references.
  • Unfinished runs are advanced under the same identity and the one-export/24-hour restriction is not bypassed.
  • Completed ZIPs are checksummed and stored in retained, private, versioned, encrypted, TLS-only DataOps storage.
  • Authenticated operators can list, start/advance, and download; unauthenticated calls are denied.
  • Portal and API expose safe status/actions without credentials, contacts, provider URLs, or raw storage identity.
  • Failure categories and logs are actionable and sanitized.
  • Read Dapier-managed Mailchimp credential from DynamoDB #120’s Dapier credential reader and exact-key read-only IAM are deployed and production-verified.
  • Agent source, test, PM, deployment, and public authorization gates passed.
  • [HUMAN] External owner confirms the Dapier mailchimp item is populated without revealing its value or storage identity.
  • [HUMAN] Post-Replace the transitional Tasks table with the final cardId schema #166 configuration references credentialId: "mailchimp" and the intended final canonical recurring Task.
  • [HUMAN] One bounded real export passes the provider, private-storage, canonical Task, controlled-download, privacy/log, and idempotency checks below.
  • [HUMAN] Only after that pass, the authorized owner explicitly disposes the old Zapier/Google Drive flow.

Preconditions and sequencing

External credential readiness

The Dapier credential owner may populate or re-save the item and record only:

  • readiness: populated;
  • provider classification: Mailchimp;
  • authorization for one DataOps acceptance run.

Do not record the key, server suffix, account identity, table name/ARN, item payload, update timestamp, or Dapier/private admin evidence. This readiness action does not authorize DataOps to call the provider.

#166 steady state

Do not run the real export during repaired A/B/C, writer quiescence, Phase D execution, or cleanup.

The export may write a run, Artifact, and canonical Task reference. Wait until:

  • Phase D has deployed the final steady-state application;
  • canonical first-write evidence has passed;
  • accepted cleanup removed transitional guards and restored ordinary push deployment;
  • runtime seeds/schedules are restored;
  • the intended recurring Task exists in the final table and its current identity is known privately.

#108 source is already an ancestor of the #166 chain and must not add to or alter A/B/C/D.

Final non-secret configuration

After #166 cleanup, an authorized configuration owner confirms one enabled Mailchimp configuration:

  • uses credentialId: "mailchimp";
  • describes account-wide audiences without contact/account-sensitive labels;
  • points to the intended final recurring Task;
  • contains no key, secret reference, provider download URL, raw storage URI, or legacy Task ID.

If configuration must change, use the ordinary reviewed main-push OIDC deployment path after #166 cleanup. Do not manually deploy or patch Lambda.

HUMAN acceptance run

1. Start and advance one run

Given the preconditions above and the legacy backup still active
When an authorized operator starts the current eligible export once
Then DataOps records requested or pending without exposing credential/provider details and disables duplicate activation while the request is in flight

If Mailchimp is still building the archive, use the scheduled invocation or the visible Advance / retry action on the same durable run. Do not create a different key, repeatedly poll with fixed sleeps, or bypass the provider’s one-export/24-hour limit.

2. Verify completion and canonical relationship

When the provider reports completion
Then the run reaches completed with safe filename/type/nonzero-size/checksum metadata, exactly one private Artifact is created, and exactly one reference appears on the intended final canonical Task

Re-advance the same completed run once. It must return the existing result without another logical provider request, object, Artifact, or Task reference. A stale/missing Task is a failed acceptance result: keep the archive, correct configuration through the ordinary path, and retry attachment without requesting another provider export.

3. Verify controlled access

From an authenticated portal session, use the controlled download action and confirm the private ZIP is obtainable only through the authorized short-lived response. Validate the archive only in an approved private environment.

Confirm the previously issued access expires according to the advertised server policy and requires a fresh authenticated action. Verify unauthenticated list, run, and download calls are denied without revealing whether an archive/account exists.

Never place the ZIP, extracted contacts, screenshot, provider response, signed URL, or storage location in this public repository, issue, CI artifact, shared .tmp/, or unmanaged drive.

4. Verify logs and privacy

Review the bounded run logs privately. They may contain safe transition/status categories only. They must not contain:

  • API key, authorization header, or Dapier payload/identifier;
  • contacts, audience members, account identity, or archive content;
  • Mailchimp signed download URL;
  • raw bucket/object identity or browser-visible s3:// URI;
  • private Task, Artifact, run, or configuration identifiers in public evidence.

5. Decide the legacy flow

Only after all checks pass may the authorized owner disable the old Zapier → Google Drive backup and revoke its obsolete access. Do not delete historical backups in this issue.

If any check fails:

  • keep the legacy flow active;
  • disable the new mailing configuration only through the ordinary authorized deployment path if required for safety;
  • preserve the private archive/run for diagnosis;
  • rotate/revoke a credential only if the credential owner determines exposure;
  • file a separate needs grooming product/security bug with sanitized failure stage and no private identifiers.

Public-safe evidence

A passing comment records only:

  • UTC verification window;
  • that the item/configuration were ready, without their identifiers;
  • final safe status (completed);
  • whether one logical request/object/Artifact/Task reference remained after replay;
  • whether controlled download and expiry/reauthorization passed;
  • whether unauthenticated denial passed;
  • whether private log review found no sensitive data;
  • whether the legacy flow was retained or disabled by its authorized owner.

Do not post exact run/configuration/Artifact/Task IDs, account labels, credential/table/bucket/object identifiers, contacts, archive metadata that identifies the account, signed URLs, headers, screenshots with private data, or provider error payloads.

Closure rule

When all HUMAN checks pass, PM may close #108 without a repository commit, provider rerun, migration/import/export test command, or application deployment.

A provider/product failure reopens implementation only through a new bounded issue. Credential absence or denied authorization remains an external blocker, not a reason for DataOps to add a fallback credential source or migration framework.

Evidence

Out of scope

Activity

  1. changed the title [-]Automate mailing-list exports and upload them to DataOps[/-] [+]Automate private Mailchimp account exports and attach them to recurring work[/+] on Jul 14, 2026
  2. added
    infraDeployment and infrastructure
    dataData model, migration, storage
    P1Important
    on Jul 14, 2026
  3. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    GROOMED

    Issue #108 is now agent-ready with product scope, API + portal behavior, privacy/security boundaries, dependencies, exact verification, and explicit out-of-scope items.

    Research notes:

    • The current uncommitted mailing-export files were reviewed only as implementation evidence, not accepted behavior. They demonstrate the likely provider/service/API/S3/task-artifact shape, but the groomed criteria also require gaps that must be resolved and tested: operator UI, authenticated controlled download, atomic concurrent idempotency, Mailchimp 24-hour/in-progress behavior, sanitized observability, stronger bucket policy/lifecycle, and complete route/provider/infrastructure/E2E coverage.
    • Mailchimp's supported Marketing API v3 operation is an account export. Requesting the audiences stage exports all audiences in the account; it is not a single-audience endpoint. The issue now makes this operator-visible and requires handling Mailchimp's one-at-a-time/one-per-24-hours constraint.
    • Real Mailchimp credential setup and a production export are marked [HUMAN]; synthetic automated verification can proceed without secrets or personal data.

    Labels: enhancement, portal, frontend, backend, infra, data, P1.

  4. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    SOFTWARE ENGINEER — READY FOR TEST

    Implemented the agent-verifiable #108 scope without calling Mailchimp or AWS and without adding any private export data.

    Summary

    • Added a provider-neutral connector registry with provider-declared export intervals and a Mailchimp Marketing API v3 adapter for account-wide audiences exports. The adapter validates/derives the server prefix, handles export_id, finished, download_url, timeouts, malformed completion, authorization, and concurrency/rate-limit responses without persisting or logging signed URLs.
    • Added deterministic durable runs in DynamoDB with conditional creation and processing leases, shared manual/scheduled idempotency, delayed polling, completed replay, Mailchimp's 24-hour limit, deterministic S3/object/artifact identities, ZIP signature validation, SHA-256/size/content type, resumable failure handling, and de-duplicated task attachment. Missing/stale tasks preserve the stored export and expose a task-link action.
    • Added authenticated sanitized list/run APIs and controlled five-minute artifact downloads. Mailing responses/task refs omit internal s3:// identities; provider secrets, lease values, and provider signed URLs are not returned.
    • Added the mailing-export operator surface to both portal frontends: loading, no-config/empty, pending, completed/download, all safe failure/action states, history, timestamps, task/artifact linkage, guarded keyboard-operable manual controls, and 390 px responsive behavior.
    • Added a retained, versioned, AES256-encrypted, public-blocked S3 bucket; TLS-only bucket policy; incomplete-upload and noncurrent-version lifecycle; scheduled EventBridge input; scoped Lambda S3/secret permissions; and optional GitHub deploy inputs. Default configuration is disabled ([]).
    • Added placeholder-only operator/provider-extension, account-wide semantics, least-privilege limitation, idempotency/retry, download, disable, and rollback documentation.

    Verification (all exit 0)

    • npm --prefix backend test — 1180/1180 passed
    • npm --prefix backend run typecheck — passed
    • npm --prefix backend run build — passed
    • npm --prefix backend run test:e2e — 344/344 passed
    • NODE_ENV=test SKIP_AUTH=true node --import tsx --test backend/tests/mailing-exports.test.ts — 11/11 passed
    • npx playwright test e2e/mailing-exports.spec.js from backend/ — 4/4 passed
    • uv run pytest tests/infra/test_template.py — 18/18 passed
    • make sam-validate — valid SAM template
    • make sam-build — build succeeded
    • node --check for both portal app files and fallback API — passed
    • git diff --check — passed

    Screenshots captured under ignored .tmp/screenshots/ and visually inspected:

    • .tmp/screenshots/mailing-exports-empty.png
    • .tmp/screenshots/mailing-exports-pending.png
    • .tmp/screenshots/mailing-exports-completed.png
    • .tmp/screenshots/mailing-exports-mobile-failures.png

    Acceptance status

    • All agent-verifiable acceptance criteria are implemented and covered by synthetic tests.
    • [HUMAN] real credential/configuration and one real private export remain intentionally open.
    • Deployment dependency: Grant the DataOps deploy role least-privilege access to the mailing-export bucket policy #118 must add the three least-privilege S3 bucket-policy management actions to the GitHub deploy role before this new TLS-only bucket policy can be shipped. No aws-infra or live AWS changes were made here.

    No commit or push performed.

  5. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    TEST FAIL — CHANGES REQUESTED

    Independent verification found a release-blocking privacy failure. No provider call, AWS write/apply, commit, push, or real/private export was performed; all runtime evidence used synthetic data.

    Required changes

    1. Internal S3 identity leaks through generic artifact mutation/attachment APIs. The mailing-specific GET/list sanitization works, but PUT /api/artifacts/{mailingArtifactId}, PUT /api/artifacts/{mailingArtifactId}/attach, and the attached task response expose the stored s3:// URI. Synthetic runtime probes returned:

      • {getStatus:200,getLeaksInternalS3:false,putStatus:200,putLeaksInternalS3:true}
      • {attachStatus:200,attachLeaksInternalS3:true,taskStatus:200,taskLeaksInternalS3:true}
        This violates acceptance criterion 8 and the explicit requirement that internal S3 identity be absent from API/UI output. Apply the mailing-artifact sanitizer to every artifact response path (create/update/attach/archive as applicable), and never put storageUri into task/bundle artifact refs for private mailing exports. Add regression tests for all generic mutation/attachment/task/bundle read paths and verify portal rendering cannot turn an internal URI into a link.
    2. The focused Playwright coverage is not the required portal/viewport matrix. backend/e2e/mailing-exports.spec.js serves only backend/src/pages/index.html; it does not execute the changed production frontend/index.html + frontend/src/app.js surface. Loading/no-config/pending/completed are checked only at desktop, while only the combined failure state is checked at 390 px, and the loading state has no screenshot. Add production-portal Playwright coverage and cover/capture loading, no-config/empty, pending/manual trigger, completed/download, and supported errors at desktop and 390 px as required.

    Commands and results

    • npm --prefix backend test — exit 0; 1180/1180 passed.
    • NODE_ENV=test SKIP_AUTH=true node --import tsx --test tests/mailing-exports.test.ts from backend/ — exit 0; 11/11 passed.
    • npm --prefix backend run typecheck — exit 0.
    • npm --prefix backend run build — exit 0.
    • uv run pytest tests/infra/test_template.py — exit 0; 18/18 passed.
    • make sam-validate — exit 0; valid template.
    • make sam-build — exit 0; build succeeded.
    • JS syntax checks for both app surfaces and fallback API plus git diff --check — exit 0.
    • npx playwright test e2e/mailing-exports.spec.js from backend/ — exit 0; 4/4 passed, but coverage gap above remains.
    • npm --prefix backend run test:e2e — exit 0; 344/344 passed.
    • Two inline node --import tsx --input-type=module -e ... synthetic runtime privacy probes — command exit 0; assertions reveal the S3 leaks above.

    Screenshots inspected

    Fresh collision-safe evidence: .tmp/screenshots/tester-108-20260714T045230Z/

    • mailing-exports-empty.png — valid desktop no-config/empty state; no error/overlap/private data.
    • mailing-exports-pending.png — valid desktop pending/manual action state; no error/overlap/private data.
    • mailing-exports-completed.png — valid desktop completed/link/download state; no raw S3 link/private data.
    • mailing-exports-mobile-failures.png — valid 390 px synthetic failure/action states; no horizontal overflow, overlap, or private data.

    These screenshots are visually clean but do not replace the missing production-frontend and full desktop/390 state matrix.

    Acceptance criteria

    1. PASS — provider-neutral contract/registry.
    2. PASS — Mailchimp account-level audiences request, prefix validation, async fields, and account-wide copy.
    3. PASS — durable deterministic run metadata; secrets/signed provider URL are not persisted.
    4. PASS — conditional run creation/lease, completed replay, deterministic artifact, and task-ref de-duplication.
    5. PASS — delayed polling and local/provider 24-hour/concurrency wait behavior.
    6. PASS — ZIP validation, SHA-256, deterministic private artifact, task-link preservation.
    7. PASS — retained/versioned/encrypted/public-blocked/TLS-only bucket, lifecycle, and scoped runtime permissions; deployment remains blocked as noted below.
    8. FAIL — generic API/task paths expose raw internal S3 identity.
    9. FAIL (evidence/coverage) — fallback screenshots are visually usable, but required production frontend and state coverage at both desktop and 390 px are missing.
    10. FAIL — failure categories/log audit shape are sanitized, but raw internal storage identity escapes through API responses.
    11. PASS — placeholder-only operator/extension/rollback documentation covers required semantics.
    12. PENDING [HUMAN] — real credential/config and one real private export/task attachment were correctly not attempted.

    Dependencies kept explicit

    Re-test after the S3 response/reference leak is fixed and the production portal + full responsive state coverage is added.

  6. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    SOFTWARE ENGINEER — CHANGES APPLIED — READY FOR RETEST

    Addressed both release-blocking findings in the Tester report: #108 (comment)

    Changes applied

    • Added a recursive HTTP response privacy boundary in backend/src/responsePrivacy.ts, applied by the Lambda handler to every JSON route response. Any internal s3:// storageUri is removed, including legacy task/bundle refs already present in persistence.
    • Hardened all generic artifact response paths (create, get, list, update, attach, and archive) so S3 identities remain server-side. Generic attachment refs omit S3 storageUri; non-S3 external/local refs retain their existing behavior. Controlled authenticated download still resolves the internal artifact server-side and returns only the short-lived download URL.
    • Expanded the backend regression to exercise mailing artifact GET/PUT/attach, task and bundle attachment/read paths, persisted new refs, legacy S3-bearing refs through the final HTTP handler boundary, and controlled download. Responses are asserted free of s3:// while internal storage remains usable.
    • Replaced the narrow fallback-only Playwright coverage with a two-surface matrix that executes both the fallback portal and the real frontend/index.html + frontend/src/app.js production surface.
    • The browser matrix covers loading, no-config/empty, pending keyboard/manual trigger with repeat-submit protection, completed/controlled download, unavailable state, and all eight safe failure categories at desktop (1280×900) and mobile (390×844). It checks horizontal overflow and raw S3 links.
    • Captures are full-page, wait for the production mobile drawer transition to finish, and use synthetic docs/API bootstrap data so screenshots contain no transient 404s or private data.

    Changed areas for this retest

    • backend/src/handler.ts
    • backend/src/responsePrivacy.ts
    • backend/src/routes/artifacts.ts
    • backend/src/mailingExports/service.ts
    • backend/tests/mailing-exports.test.ts
    • backend/e2e/mailing-exports.spec.js

    The rest of the original #108 implementation remains in the shared uncommitted worktree. No source repo, provider, AWS, commit, or push operation was performed.

    Verification

    All listed successful commands exited 0:

    • npm --prefix backend test — 1180/1180 passed
    • npm --prefix backend run typecheck — passed
    • npm --prefix backend run build — passed
    • NODE_ENV=test SKIP_AUTH=true node --import tsx --test tests/mailing-exports.test.ts from backend/ — 11/11 passed
    • npx playwright test e2e/mailing-exports.spec.js --workers=1 --reporter=dot from backend/ — 2/2 passed after the final capture/navigation refinements
    • npx playwright test --reporter=dot from backend/ — isolated full suite 342/342 passed in 2.6m
    • uv run pytest tests/infra/test_template.py — 18/18 passed
    • make sam-validate — valid SAM template
    • make sam-build — build succeeded
    • node --check frontend/src/app.js, node --check backend/src/public/app.js, and node --check backend/src/public/api.js — passed
    • git diff --check — passed

    One earlier full-E2E attempt was invalid because it was run concurrently with sam build, whose custom builder performs root npm ci; this replaced dependencies while Playwright was active. It was discarded and rerun alone, producing the 342/342 result above.

    Screenshot evidence

    Fresh synthetic evidence is under the gitignored directory:

    • .tmp/screenshots/issue-108-engineer-retest/
    • 24 state images: {fallback,production}-{desktop,mobile-390}-{loading,empty,pending,completed,errors,unavailable}.png

    All 24 were generated and inspected. The production shell and fallback surface render without horizontal overflow at 390 px; loading/empty/pending/completed/unavailable/error states are readable; controls remain usable; no provider data, contacts, signed provider URL, or raw s3:// link appears.

    Acceptance impact

    • Criterion 8: fixed — mutation, attachment, task, bundle, list/get, and download response paths now enforce the internal-storage boundary.
    • Criterion 9: fixed — production and fallback surfaces now have the complete desktop/390 state and interaction matrix.
    • Criterion 10: fixed — safe error categories remain intact and the final response boundary prevents legacy/internal S3 identity from escaping.
    • All other agent-verifiable criteria remain passing.
    • Grant the DataOps deploy role least-privilege access to the mailing-export bucket policy #118 is now closed, so the prior deploy-role source dependency is resolved.
    • [HUMAN] remains intentionally pending: supply the real Mailchimp configuration through the approved mechanism and verify one real private export/task attachment. Keep Automate private Mailchimp account exports and attach them to recurring work #108 open with human until that check is complete.

    No commit or push performed.

  7. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    TEST PASS — independent retest

    The two prior release blockers are resolved. No source file was edited, and no provider call, AWS write/apply, commit, push, or real/private export was performed during this retest. All runtime/browser evidence used synthetic data.

    Commands and results

    • npm --prefix backend test — exit 0; 1180/1180 passed.
    • NODE_ENV=test SKIP_AUTH=true node --import tsx --test tests/mailing-exports.test.ts from backend/ — exit 0; 11/11 passed.
    • npm --prefix backend run typecheck — exit 0.
    • npm --prefix backend run build — exit 0.
    • uv run pytest tests/infra/test_template.py — exit 0; 18/18 passed.
    • make sam-validate — exit 0; valid SAM template.
    • make sam-build — exit 0; build succeeded.
    • node --check frontend/src/app.js, node --check backend/src/public/app.js, node --check backend/src/public/api.js, and git diff --check — exit 0.
    • npx playwright test e2e/mailing-exports.spec.js --workers=1 --reporter=list from backend/ — exit 0; 2/2 passed. Each test covers one real surface (fallback and production), both desktop and 390 px, and the full loading/empty/pending/manual/completed/download/error/unavailable matrix.
    • npm --prefix backend run test:e2e — exit 0; isolated full suite 342/342 passed in 2.6 minutes.
    • Independent inline synthetic node --import tsx --input-type=module -e ... privacy/integrity probe — exit 0.

    Former S3 leak: PASS

    The independent runtime probe created one synthetic private mailing artifact and exercised the former leak paths. Structural results:

    • generic artifact PUT: 200; raw s3:// absent;
    • generic artifact archive: 200; raw s3:// absent;
    • generic artifact attach to task + bundle: 200; raw s3:// absent;
    • newly persisted task and bundle artifact refs: raw s3:// absent;
    • deliberately injected legacy S3-bearing task and bundle refs read through the Lambda HTTP edge: raw s3:// absent;
    • controlled artifact download: 200, exactly 300-second expiry, expected synthetic signed-download response.

    Focused tests also confirm list/get/replay auth, unauthenticated 401s, delayed polling, conditional concurrent idempotency, deterministic artifact/ref de-duplication, ZIP integrity/checksum, storage retry, task-link preservation, the 24-hour wait state, and provider response parsing. Source review confirms audit logs include stable config/run/status/transition/category fields only, not Authorization headers, secrets, signed provider URLs, contacts, or storage identity.

    Browser matrix and screenshots: PASS

    Fresh collision-safe evidence is in .tmp/screenshots/tester-108-retest-20260714T052546Z/.

    There are 24 source screenshots: {fallback,production}-{desktop,mobile-390}-{loading,empty,pending,completed,errors,unavailable}.png.

    • Desktop source images are 1280 px wide; mobile source images are exactly 390 px wide.
    • Playwright asserted no horizontal overflow, one keyboard-triggered request, disabled repeat-submit control, controlled-download copy, all eight safe failure categories, and no raw S3 browser link.
    • I visually inspected all 24 using four contact sheets and full-size error/mobile-completed images. Loading, no-config/empty, pending/manual action, completed/link/download, all error actions, and unavailable states are readable on both surfaces and viewports. No 404/error shell, clipped controls, text overlap, horizontal overflow, private/contact data, provider signed URL, or raw S3 link is present.

    Acceptance criteria

    1. PASS — provider-neutral connector contract and registry.
    2. PASS — account-wide Mailchimp audiences request, credential/server validation, async fields, and accurate operator semantics.
    3. PASS — deterministic complete durable metadata; no persisted provider signed URL or secret.
    4. PASS — shared manual/scheduled service, conditional lease/idempotency, replay, and artifact/ref de-duplication.
    5. PASS — durable polling and provider/local 24-hour/concurrency wait behavior.
    6. PASS — ZIP validation, SHA-256, deterministic private object/artifact, and safe task-link handling.
    7. PASS — retained/versioned/encrypted/public-blocked/TLS-only bucket, lifecycle, and scoped runtime permissions.
    8. PASS — authenticated sanitized API, 401s without auth, controlled five-minute download, and no credential/signed-provider/raw-S3 exposure across generic or legacy response paths.
    9. PASS — complete production + fallback state/action matrix at desktop and 390 px with guarded keyboard controls.
    10. PASS — all failure categories and safe operator actions; sanitized stable audit shape.
    11. PASS — placeholder-only provider/operator/extension/disable/rollback documentation.
    12. PENDING [HUMAN] — a credentialed operator must supply the real Mailchimp configuration and verify one real private export/task attachment. Keep Automate private Mailchimp account exports and attach them to recurring work #108 open with human until complete.

    Dependency status

    #118 is now closed after the credentialed CloudFormation apply and source/live reconciliation completed IN_SYNC; the prior deploy-role blocker is resolved. This Tester verdict does not claim the remaining real-provider [HUMAN] check.

    The agent-verifiable implementation is approved for PM acceptance.

  8. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    REJECTED — Product Manager final acceptance

    The Tester evidence is complete and clean, #118 is closed, and I accept the implementation's account-wide Mailchimp semantics, 24-hour/concurrency handling, pending/retry and safe failure copy, authenticated controlled download, response privacy boundary, infrastructure/privacy documentation, and the explicit remaining [HUMAN] real-provider check.

    One release-blocking operator-flow defect remains on both portal surfaces:

    • A completed run renders a button labelled “Start daily export”, but the button keeps run.runKey from that completed run (frontend/src/app.js and backend/src/public/app.js). The fresh completed-state fixture demonstrates the mismatch: the displayed completed run is keyed 2026-07-13 while the tested current day is 2026-07-14. Clicking the control therefore replays the already-completed durable result instead of starting/advancing today's eligible run. The operator receives no indication that nothing new was requested.
    • This fails the portal manual-run journey and the requirement that controls expose the correct next action. It also makes the “Start daily export” copy misleading.

    Required fix and retest:

    1. For no-run or completed states, submit a fresh current-day stable run key; preserve the existing run key only for requested/pending/failed states that must advance or retry the same durable run. Alternatively, change the completed-state action/copy to an equally clear behavior that actually matches what it submits.
    2. Add Playwright assertions on both production and fallback surfaces proving the completed-state button submits today's key (while pending/failed retries preserve their original key), guards repeat submission, and renders the resulting 24-hour wait/completed response honestly at desktop and 390 px.
    3. Re-capture and inspect the affected completed/action state.

    The 24 fresh screenshots under .tmp/screenshots/tester-108-retest-20260714T052546Z/ otherwise pass PM visual review: loading, empty, pending, completed/download, all eight error categories, and unavailable states are readable and privacy-safe at desktop and exactly 390 px, with no overlap, horizontal overflow, raw S3 URI, provider URL, contact data, or broken shell.

    The remaining real Mailchimp secret/configuration and one real private export/task attachment stay correctly outside agent acceptance as [HUMAN]. No provider or AWS action should be taken as part of this fix.

  9. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    SOFTWARE ENGINEER — PM FIX APPLIED — READY FOR RETEST

    Addressed the final-acceptance rejection: #108 (comment)

    Operator-flow fix

    • Both portal surfaces now derive the action run key from state:
      • no existing run or completed → a fresh, stable current UTC-day key;
      • requested, pending, or failed → the existing persisted run key, so advance/retry resumes the same durable run.
    • Therefore Start daily export no longer replays the previous completed day. Advance / retry continues the exact pending/failed run as intended.
    • The production implementation is in frontend/src/app.js; the fallback implementation is in backend/src/public/app.js.

    Browser regression coverage

    backend/e2e/mailing-exports.spec.js now inspects the actual POST JSON body on both portal surfaces at desktop and exactly 390 px:

    • no-job start submits today's key;
    • the resulting pending retry preserves its original key;
    • a completed run keyed 2026-07-13 submits today's key rather than replaying 2026-07-13;
    • a failed run keyed 2026-07-12 preserves 2026-07-12 on retry;
    • each action is disabled while its request is active, guarding repeat submission;
    • completed → new-day start renders the returned provider 24-hour-window wait state honestly.

    Screenshot evidence

    Fresh synthetic captures remain under the ignored directory .tmp/screenshots/issue-108-engineer-retest/.

    The affected before/after matrix was recaptured and inspected:

    • {fallback,production}-{desktop,mobile-390}-completed.png
    • {fallback,production}-{desktop,mobile-390}-completed-started.png

    All eight images are readable and privacy-safe with no overlap or horizontal overflow. The completed view offers the existing controlled archive download and Start daily export; after starting, the view shows today's pending run, safe provider-concurrency copy, and the earliest retry time. No S3 URI, provider URL, contact data, or real account data appears.

    Verification

    All successful commands exited 0:

    • npm --prefix backend test — 1180/1180 passed
    • npm --prefix backend run typecheck — passed
    • npm --prefix backend run build — passed
    • npx playwright test e2e/mailing-exports.spec.js --workers=1 --reporter=dot from backend/ — 2/2 passed
    • npx playwright test --reporter=dot from backend/, run alone after SAM build — 342/342 passed in 2.7m
    • uv run pytest tests/infra/test_template.py — 18/18 passed
    • make sam-validate — valid SAM template
    • make sam-build — build succeeded
    • JavaScript syntax checks for production app, fallback app, and fallback API — passed
    • git diff --check — passed

    No provider call, AWS write/apply, commit, or push was performed. The real Mailchimp configuration/export remains the explicit [HUMAN] acceptance item.

  10. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    TEST PASS — PM run-key fix retest

    The completed-run replay defect is fixed on both portal surfaces. No source file was edited, and no provider call, AWS write/apply, commit, push, or real/private export was performed. All evidence is synthetic.

    Commands and results

    • npm --prefix backend test — exit 0; 1180/1180 passed.
    • NODE_ENV=test SKIP_AUTH=true node --import tsx --test tests/mailing-exports.test.ts from backend/ — exit 0; 11/11 passed.
    • npm --prefix backend run typecheck — exit 0.
    • npm --prefix backend run build — exit 0.
    • JavaScript syntax checks for production app, fallback app, and fallback API plus git diff --check — exit 0.
    • npx playwright test e2e/mailing-exports.spec.js --workers=1 --reporter=list from backend/ — exit 0; 2/2 passed.
    • npm --prefix backend run test:e2e — exit 0; isolated full suite 342/342 passed in 2.6 minutes.
    • Independent ignored .tmp Playwright probe with a deliberately stale pending key across fallback/production × 1280/390 — exit 0; 4/4 preserved the exact existing key.

    An initial version of the optional .tmp probe timed out because its Tester-authored assertion incorrectly depended on document.activeElement after DOM replacement. It did not reveal a product failure; I corrected only the ignored probe assertion and reran it successfully as recorded above.

    Infrastructure/SAM tests were not repeated for this app-only follow-up: the immediately preceding independent #108 retest already passed 18/18 infrastructure tests plus SAM validate/build, and this fix changes only portal run-key selection and Playwright coverage.

    Submitted-body matrix: PASS

    The focused test inspects the actual JSON sent to /api/mailing-exports/run for both the fallback and real production portals at desktop and exactly 390 px:

    • no prior job → current stable UTC day (new Date().toISOString().slice(0, 10));
    • completed prior job keyed 2026-07-13 → current UTC day, not 2026-07-13;
    • pending job → its existing durable run key on advance;
    • failed job keyed 2026-07-12 → exactly 2026-07-12 on retry;
    • independent stale-pending probe keyed 2026-07-11 → exactly 2026-07-11 in all four surface/viewport combinations.

    Every action was keyboard-triggered, disabled while in flight, and produced one POST per activation. After starting from the completed state, both portals rendered the returned current-day pending run with safe provider 24-hour-window copy and earliest retry guidance.

    Screenshots: PASS

    Fresh collision-safe evidence: .tmp/screenshots/tester-108-runkey-20260714T054508Z/.

    Eight images were inspected:

    • {fallback,production}-{desktop,mobile-390}-completed.png
    • {fallback,production}-{desktop,mobile-390}-completed-started.png

    The before state clearly offers controlled download plus Start daily export; the after state shows the new pending run, provider-concurrency explanation, retry time, and Advance / retry. All are readable, controls are intact, mobile images are exactly 390 px wide with no horizontal overflow, and there is no 404/error shell, overlap, raw S3 URI, provider signed URL, contact data, or real account data.

    Privacy regression: PASS

    The focused 11-test mailing suite remains green, including generic artifact PUT/attach/task/bundle response sanitization, persisted/legacy reference redaction at the HTTP edge, authenticated five-minute controlled download, unauthenticated 401s, signed-provider-URL redaction, concurrent idempotency, and checksum/integrity behavior. The run-key change does not alter the established storage/privacy boundary.

    All agent-verifiable #108 criteria now pass. The real Mailchimp configuration and one real private export/task attachment remain the explicit [HUMAN] item; keep #108 open with human until that check completes.

    Approved for PM re-acceptance.

  11. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    ACCEPTED — Product Manager final acceptance

    The completed-run replay defect is resolved on both the production and fallback portal surfaces.

    PM verification:

    • No prior run and completed prior runs now submit the stable current UTC-day key. A completed 2026-07-13 run correctly submits the current 2026-07-14 key instead of replaying the old result.
    • Requested, pending, and failed runs preserve their existing durable run key, including the independently probed stale pending key, so advance/retry continues the original provider job.
    • Both portal implementations disable the activated control while the request is in flight. The browser evidence proves one POST per activation and keyboard operation at desktop and exactly 390 px.
    • After a completed-state start, both portals honestly render the new pending run with the safe provider-concurrency explanation, earliest retry time, and Advance / retry action.
    • The eight fresh screenshots in .tmp/screenshots/tester-108-runkey-20260714T054508Z/ pass visual review. Completed/download and completed-started/wait states are readable on both surfaces and viewports, with intact controls, no overlap or horizontal overflow, and no raw S3 URI, provider signed URL, contact data, real account data, or broken shell.
    • The final Tester PASS includes the focused request-body matrix, repeat guards, 1180/1180 backend tests, 11/11 mailing tests, typecheck/build, 342/342 full E2E tests, and the independent 4/4 stale-pending-key probe.
    • Previously accepted criteria remain satisfied: provider-neutral architecture; account-wide Mailchimp audiences semantics; one-at-a-time and 24-hour handling; deterministic idempotency and polling; private retained storage/artifact/task linkage; authenticated five-minute downloads; sanitized API/log/error behavior; responsive portal states; and placeholder-only extension, configuration, disable, and rollback documentation.
    • Dependency Grant the DataOps deploy role least-privilege access to the mailing-export bucket policy #118 is closed and source/live reconciled.

    All agent-verifiable acceptance criteria pass. The only remaining boundary is the issue's explicit [HUMAN] check: a credentialed operator must supply the real Mailchimp configuration through the approved mechanism and verify one real private export reaches DataOps storage and its intended recurring task. No provider or AWS action belongs in this acceptance stage. Keep #108 open with human after shipping until that real-provider check is completed.

    No edits, commit, push, provider call, or AWS write were performed during PM acceptance.

  12. added
    humanCode done or issue blocked on human verification
    on Jul 14, 2026
  13. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    ENGINEERING COMMIT COMPLETE — [HUMAN] PRODUCTION VERIFICATION CHECKLIST

    Committed the accepted agent-verifiable implementation as e527164429e60c573cd14162a3e9020bd3184d16 (Refs #108). The issue remains open and now has the human label. Nothing was pushed and no provider/AWS action was performed.

    A credentialed operator should complete the following checklist after this commit is pushed and the normal GitHub Actions OIDC deployment has successfully updated the DataOps stack. Do not manually deploy the application and do not paste any real value into this public issue, task content, logs, or screenshots.

    1. Credential and private configuration

    • Create or select a dedicated Mailchimp Marketing API key for the account whose account-wide audiences stage must be backed up. Confirm the key suffix identifies the expected server prefix.
    • Store only { "apiKey": "<real-key>", "server": "<matching-prefix>" } in an AWS Secrets Manager secret. server may be omitted when the API-key suffix is authoritative. Record the secret ARN privately.
    • Set the repository/environment secret MAILCHIMP_SECRET_ARN to that ARN through the approved GitHub/AWS operator path.
    • Identify the intended existing recurring task/run in the authenticated DataOps portal/API and record its task ID privately. Do not create a placeholder task solely to make the check pass.
    • Set the private MAILING_EXPORTS_CONFIG variable to one enabled configuration with a non-sensitive configuration ID/account label, provider: "mailchimp", scopeLabel: "All audiences (account export)", the intended taskId, and secretName resolving to the same authorized secret. Do not include contacts, audience members, the API key, or signed URLs.
    • Let the normal CI/CD workflow deploy the parameters. Confirm CloudFormation completes successfully and the Lambda environment names the private retained mailing-export bucket; do not expose the bucket name publicly.

    2. Real export

    • Sign in as an authorized DataOps operator and open Mailing exports. Confirm the configured account appears without credential material or raw storage identity.
    • Click Start daily export once. Confirm the submitted run is for the current UTC-day key and that the control prevents a duplicate submission while active.
    • If Mailchimp is still building the archive, confirm the run remains pending with the same provider export/run identity and a wait/advance action. Refresh or advance the same run later; do not create a new key to bypass the one-export/24-hour restriction.
    • Confirm the run reaches completed with a ZIP filename, nonzero size, application/zip, SHA-256 checksum, artifact ID, requested/completed timestamps, and no provider download_url in the API, page, task, or logs.
    • Confirm exactly one logical object/artifact exists after replaying the same completed run key; replay must not request another provider export or create another artifact.

    3. Task attachment

    • Open the intended recurring task/run and confirm exactly one artifact reference was attached for the completed export.
    • Confirm the task reference contains the artifact identity/status but no raw s3:// URI or browser-accessible provider URL.
    • Re-run/advance the same completed key and confirm the task still has one reference, not a duplicate.
    • If the task link reports stale/missing, correct the configured task ID and retry the same run. Confirm the already stored archive is retained and attached rather than downloaded/uploaded again.

    4. Controlled download

    • Use Download ZIP from the authenticated portal. Confirm DataOps returns a short-lived link (five minutes), the download succeeds, and the browser never receives a raw s3:// URI or Mailchimp signed download_url.
    • Inspect the ZIP only in an approved private location. Confirm it is a valid account-level audiences export; do not upload it to GitHub, CI artifacts, this issue, .tmp/ on a shared host, Google Drive, or another public/unmanaged location.
    • After five minutes, confirm the prior controlled URL is expired/unusable and a new authenticated download action is required.
    • Confirm an unauthenticated list, run, and artifact-download attempt receives 401/403 and cannot infer account/export contents.

    5. Logs, legacy-flow shutdown, and revocation

    • Review the Lambda and scheduler logs for this run. Logs may contain stable configuration/run IDs, statuses, transitions, and safe error categories only. Confirm there are no API keys, Authorization headers, contact/audience data, secret payloads/ARN values, provider signed URLs, raw archive content, or raw S3 browser links.
    • Confirm the scheduled EventBridge invocation can advance the unfinished/completed workflow without duplicating the provider request, object, artifact, or task reference.
    • Only after the real export, task attachment, controlled download, and safe-log checks pass, disable the legacy Zapier → Google Drive backup automation and revoke its Mailchimp/Google access. Do not delete historical backups yet.
    • Keep the new Secrets Manager credential active for DataOps scheduling. If a temporary validation key was used, rotate the secret to the approved ongoing key first, verify one authenticated status/run operation, and then revoke the temporary key.
    • If verification fails, set the configuration enabled to false and redeploy via CI. Preserve the retained bucket/artifact history, keep the legacy flow active, rotate/revoke any exposed or suspect key, and record only the safe failure category in this issue.

    When every item passes, post a redacted confirmation containing only the run/configuration IDs, completion time, artifact ID, intended task ID/link status, download-expiry result, log-review result, and legacy credential revocation result. Do not post account contacts, archive contents, bucket/object URI, secret identifiers, keys, or signed URLs.

  14. alexeygrigorev commented on Jul 14, 2026

    @alexeygrigorev
    MemberAuthor

    ON-CALL DEPLOYMENT STATUS — commit e527164

    The mailing-export implementation is contained in current main and in the latest successfully deployed artifact.

    This confirms deployment and the public authorization boundary only. No provider call, export, artifact creation, task attachment, download, secret rotation, or legacy-flow shutdown was performed. Keep the issue open with human until its credentialed provider checklist is completed.

    ON-CALL DONE: #108 code/configuration path is deployed; real provider verification remains human-owned.

  15. alexeygrigorev commented on Jul 31, 2026

    @alexeygrigorev
    MemberAuthor

    PM TRACKER RECONCILIATION

    The issue body now reflects shipped state rather than reopening implementation:

    • Agent-verifiable criteria are checked from the independent Tester passes, final PM acceptance, accepted commit e527164, and successful On-Call deployment evidence.
    • The obsolete Mailchimp Secrets Manager setup is superseded by Read Dapier-managed Mailchimp credential from DynamoDB #120: deployed configuration uses a non-secret Dapier credentialId, with exact-table/leading-key-constrained GetItem and no Mailchimp Secrets Manager grant. Read Dapier-managed Mailchimp credential from DynamoDB #120 commit 0267050 passed its full lifecycle and deployment.
    • The human label remains. No credential was inspected, no provider call was made, and no HUMAN criterion is claimed passed.

    Current blocker: #120 last reported the Dapier-managed mailchimp item empty. Resume only after a credentialed operator posts sanitized confirmation that the item is populated and the deployed mailing-export configuration references its non-secret credentialId; then complete the unchecked real export, task attachment, controlled download, privacy/log, and legacy-flow checks.

    Evidence: Tester pass, PM acceptance, commit handoff, #108 On-Call, #120 On-Call.

  16. alexeygrigorev commented on Aug 13, 2026

    @alexeygrigorev
    MemberAuthor

    PM runtime-acceptance reconciliation complete.

    Decision: #108 remains open/blocked as the single HUMAN Mailchimp runtime tracker. Agent/source work is complete.

    Dependencies are now exact:

    Exact next owners:

    1. Dapier credential owner may post sanitized readiness that the mailchimp record is populated and authorize one acceptance run—without exposing its value or storage identity.
    2. Orchestrator holds the run until Replace the transitional Tasks table with the final cardId schema #166 steady state.
    3. Authorized DataOps configuration owner confirms credentialId: "mailchimp" and the intended final recurring Task after reset/seeding.
    4. Authorized mailing-export operator runs one bounded export and verifies same-run polling, one private object/Artifact/Task reference, replay idempotency, controlled download/reauthorization, unauthenticated denial, and private safe logs.
    5. Only after PASS may the authorized owner dispose the old Zapier/Google Drive flow.

    The public evidence contract now forbids exact production run/config/Artifact/Task IDs, account labels, table/bucket/object identifiers, contacts, archive data, signed URLs, credentials, headers, and private screenshots. A safe result is enough.

    No second export is needed for #120, no one-off test/import/export/restore command is part of acceptance, and no source commit/deploy is needed to close #108 after the HUMAN pass.

    No repository file, test, AWS/provider call, credential read/write, data mutation, import/export/restore, deployment, commit, or push was performed.

  17. alexeygrigorev commented on Oct 5, 2026

    @alexeygrigorev
    MemberAuthor

    Superseded by #246. Dapier now owns Mailchimp mailing-list export; DataOps shipped the complete deletion of this capability on f38578e.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1ImportantbackendBackend/APIdataData model, migration, storageenhancementNew or improved functionalityfrontendFrontend UIhumanCode done or issue blocked on human verificationinfraDeployment and infrastructureportalShared portal shell and UX

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions