Skip to content

Modernize testlead Lambda: Node 24, SDK v3, osls deploy (sc-112520)#15

Merged
johnrb2 merged 29 commits into
masterfrom
chore/sc-112520-upgrade-testlead-node24
Jul 15, 2026
Merged

Modernize testlead Lambda: Node 24, SDK v3, osls deploy (sc-112520)#15
johnrb2 merged 29 commits into
masterfrom
chore/sc-112520-upgrade-testlead-node24

Conversation

@johnrb2

@johnrb2 johnrb2 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Description of the change

Brings the test-sales-and-dev-leads Lambda up to date (it was flagged as a Node 20 straggler) and modernizes the surrounding tooling, dependencies, secret handling, and deploy authentication.

Runtime & deployment

  • Upgrades the Lambda runtime from nodejs20.x to nodejs24.x and pins engines.node >= 24.
  • Adds serverless.yml and a workflow_dispatch GitHub Action (deploy-staging.yml) that deploys via osls, codifying the runtime, S3 read permission, secretsmanager:GetSecretValue on the demo-keys secret, every-minute schedule, and the DEMO_KEYS_SECRET_ID env var (the Secrets Manager secret id only; the keys are fetched at runtime).
  • Adds a CI workflow (test.yml) that lints, runs the test suite, and validates the serverless template (serverless package).
  • Bumps release.yml to Node 24.

Dependency modernization

  • aws-sdk v2 → @aws-sdk/client-s3 v3.
  • request@activeprospect/request-capture/request (callback API wrapped in a small requestAsync promise helper).
  • faker@faker-js/faker (pinned to 8.4.1, the last CJS-compatible release) with the corresponding API renames.
  • moment removed in favor of native Date.

Behavior / refactor

  • The Lambda handler and the lead/feedback submission paths are now fully async/await. The handler awaits both the S3 reads and every downstream post (via Promise.all), so its promise only settles once all work is complete — correct for an async Lambda handler, which freezes the execution environment as soon as the promise resolves.
  • S3 config-load and submission-processing are kept in separate try/catch blocks so a failed read is reported distinctly from a processing failure.

Linting (ESLint)

  • Adopts leadconduit-api's ESLint setup, extending semistandard (semicolons required) with complexity capped at 10. Applies eslint --fix across the repo and enforces npm run lint in CI. Hand-fixes include refactoring fakeByLCName to a lookup map, fixing a latent implicit-global fields assignment, and replacing a top-level return in testfeedback.

Demo keys → Secrets Manager (Doppler-sourced, runtime fetch)

  • Replaces the bundled, gitignored demoConfig/keys.json with lib/demokeys.js getDemoKeys(), resolving in precedence order: DEMO_KEYS env (inline JSON) → local file → AWS Secrets Manager, with a "log in to AWS" fallback that prints actionable guidance on a credentials error. Tunable via DEMO_KEYS, DEMO_KEYS_FILE, DEMO_KEYS_SECRET_ID, DEMO_KEYS_SOURCE, DEMO_KEYS_DISABLE_AWS.
  • The Lambda fetches the keys from Secrets Manager at runtime via its function role (secretsmanager:GetSecretValue); the secret is never placed in the Lambda environment (only the non-sensitive DEMO_KEYS_SECRET_ID). Doppler is the source of truth via a Doppler → Secrets Manager sync (project leadconduit-lambdas). @aws-sdk/client-secrets-manager is lazy-required so the env/file paths never load it, and demoConfig/** is excluded from the deploy artifact so a stray local keys.json can't shadow Secrets Manager.
  • serverless.yml no longer resolves any deploy-time variable, so serverless package runs credential-free in CI and a real deploy carries no secret material.

Deploy auth → GitHub OIDC

  • deploy-staging.yml now assumes a dedicated least-privilege testlead-deployer role via GitHub OIDC (aws-actions/configure-aws-credentials), removing the static AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY secrets and the TESTLEAD_DEMO_KEYS keys.json write step. NPM_TOKEN is the only secret the workflow needs.

Cleanup

  • Removes the retired deploy.sh. README documents the Secrets Manager / Doppler keys model, local getDemoKeys() resolution, OIDC deploy, and the local osls deploy break-glass path.

Tests

  • Characterization suite (mocha + chai + nock) covering submitlead, submitfeedback, and the index handler, plus test/demokeys.test.js for the resolver (env/file/Secrets Manager/auth-failure/forcing). 47 passing.

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Chore
  • Configuration change
  • Technical Debt
  • UI Change Only
  • Documentation

Related tickets

sc-112520

Deployment note — prerequisites (DevOps)

This PR is gated on DevOps provisioning (tracked in sc-112598):

  1. NPM_TOKEN GitHub Actions secret — required by CI (npm ci of the private @activeprospect/request-capture). CI stays red until this is set.
  2. AWS Secrets Manager secret leadconduit-lambdas/staging/test-sales-and-dev-leads (SecretString = account name → API key JSON), Doppler-sourced via a Doppler → Secrets Manager sync, in the staging account (371005981288, us-east-1). If the sync encrypts with a customer-managed KMS key, the function role also needs kms:Decrypt on that key. The secret id is overridable via the DEMO_KEYS_SECRET_ID env var in serverless.yml.
  3. The testlead-deployer OIDC role + trust policy in the staging account (371005981288), with permission to create the function role's secretsmanager:GetSecretValue statement.

Deployment note — one-time cutover

The existing function and its every-minute EventBridge rule (test-sales-and-staging-leads, single target = this Lambda) were created outside CloudFormation, so the first osls deploy cannot adopt them. Before the first deploy, in the LeadConduit staging account (us-east-1):

  1. Delete the manual EventBridge rule test-sales-and-staging-leads.
  2. Delete the existing test-sales-and-dev-leads function.
  3. Run the deploy workflow — CloudFormation recreates both, fully stack-managed.

Residual risks (accepted)

  • @aws-sdk/client-secrets-manager is traced into the Lambda bundle by the include-dependencies plugin. Unlike the previous SSM client, it is actually used at runtime, so this is expected; the only cost is bundle size.
  • The Lambda calls GetSecretValue on every invocation (once per minute, ~43k/month) with no in-process caching — negligible against Secrets Manager cost/throttle limits.
  • The deploy workflow cannot succeed until the OIDC role and the Secrets Manager secret exist (above). It is workflow_dispatch-only, so merging is safe; CI (lint/test) is independent once NPM_TOKEN is set.

Checklists

Development and Testing

  • Lint rules pass locally.
  • The code changed/added as part of this pull request has been covered with tests, or this PR does not alter production code.
  • All tests related to the changed code pass in development, or tests are not applicable.

Code Review

  • This pull request has a descriptive title and information useful to a reviewer. There may be a screenshot or screencast attached.
  • At least two engineers have been added as "Reviewers" on the pull request.
  • Changes have been reviewed by at least two other engineers who did not write the code.
  • This branch has been rebased off master to be current.

Tracking

  • Issue from Shortcut/Jira has a link to this pull request.
  • This PR has a link to the issue in Shortcut.

QA

  • This branch has been deployed to staging and tested.

Made with Cursor

johnrb2 and others added 12 commits June 29, 2026 12:07
Add mocha/chai/nock tests covering submitlead, submitfeedback, and the
lambda handler to lock in current behavior before modernizing dependencies.
Export generateLead and fakeByLCName so they can be unit tested directly.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Migrate the S3 config reads in the lambda handler from the end-of-support
aws-sdk v2 to the modular @aws-sdk/client-s3 v3 client. Convert the handler
to async/await so it awaits both reads, and drop the hardcoded region so the
SDK uses AWS_REGION from the Lambda runtime. Update the index test to mock
the v3 S3Client.send.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Swap the deprecated request module for the request-capture compat shim, which
exposes the same callback API backed by native node:http/node:https. Only the
require specifier changes; all existing call sites are unchanged.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Swap the unmaintained faker package for the community @faker-js/faker fork
(pinned to 8.4.1, the last CJS-compatible release). Update all generator
calls to the v8 namespaces (person/location/string/helpers) and the object
form of date.past so no deprecated APIs are hit on the per-minute schedule.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
The only moment usage was a 7-day lookback date formatted as YYYY-MM-DD.
Replace it with native Date arithmetic and toISOString().slice(0, 10),
dropping the moment dependency entirely. Lambda runs in UTC so the formatted
date is unchanged.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Codify the test-sales-and-dev-leads Lambda runtime (nodejs24.x) and its
schedule, S3 IAM, and packaging in serverless.yml, modeled on the
automated-retries service. The function keeps its existing name so osls
takes over the current function rather than creating a duplicate.

Add a workflow_dispatch deploy workflow (osls deploy --stage staging) and a
test workflow that runs the unit tests and validates that the serverless
template packages on every push/PR. Both materialize demoConfig/keys.json
(real keys from a secret on deploy, a placeholder for packaging validation)
because the bundling plugin statically resolves that require.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Bump the npm publish workflow to Node 24 to match the Lambda runtime, declare
"engines": { "node": ">=24" } in package.json, and mark deploy.sh as deprecated
in favor of the osls deploy workflow.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
An async handler completes as soon as its promise settles, which on Lambda
freezes the execution environment before the callback-based HTTP posts in
submitLead/submitFeedback finish. Use .then() chains and keep lambda
synchronous so the default callbackWaitsForEmptyEventLoop drains the event
loop, matching the original aws-sdk v2 behavior. Poll for side effects in the
test since the handler no longer returns a promise.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
faker v4's name.title() produced job titles; the v8 equivalent is
person.jobTitle(), not person.prefix() (which yields honorifics like "Mr.").
Map the LeadConduit title field to jobTitle to preserve the original
semantics.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Promisify submitLead and submitFeedback (wrapping the request-capture callback
in a small requestAsync helper) and have demoLeads/demoFeedbacks await every
submission via Promise.all. The lambda handler is async again and awaits both
the S3 reads and all downstream posts, so the handler promise only settles
once the work is complete rather than relying on event-loop draining. Update
tests to await the promises instead of polling.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
- Separate the S3 config-load and submission-processing phases in the lambda
  handler so a failed read is logged distinctly from a failure while processing
  a successfully fetched config (previously a submission error was mislabeled as
  an S3 load failure).
- Guard verbose response logging in submitLead against a missing response
  object, which on a request error (err set, response undefined) would throw a
  TypeError and reject Promise.all, skipping the remaining submissions.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the deploy.sh reference in the README with the osls/GitHub Actions
workflow, and spell out the one-time cutover (delete the manual EventBridge
rule and existing function) required before the first CloudFormation-managed
deploy.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Modernizes the test-sales-and-dev-leads Lambda and its tooling by upgrading to Node 24, moving AWS SDK usage to v3, codifying deployment via Serverless/osls, and adding characterization tests to lock in existing behavior during the async refactor.

Changes:

  • Upgrade runtime/tooling to Node 24 and update dependencies (AWS SDK v3, request wrapper, faker, remove moment).
  • Add serverless.yml + GitHub Actions workflows for CI testing, serverless packaging validation, and manual staging deploy via osls.
  • Refactor handler + submission flows to async/await and add mocha/chai/nock characterization tests.

Reviewed changes

Copilot reviewed 13 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
test/submitlead.test.js Adds characterization tests for fake field generation, lead generation, and lead submission behavior.
test/submitfeedback.test.js Adds characterization tests for feedback query/post flow and staging host selection.
test/index.test.js Adds tests for Lambda S3 config reads and basic processing logging.
serverless.yml Codifies Lambda runtime (nodejs24.x), schedule, and S3 read IAM permissions.
README.md Documents new osls-based deployment workflow and one-time cutover steps.
package.json Pins Node engine to >=24, adds test script, modernizes dependencies and adds devDependencies for tests.
package-lock.json Updates lockfile to v3 and captures the new dependency graph.
lib/submitlead.js Replaces deprecated deps, updates faker API usage, and converts submission flow to async/await.
lib/submitfeedback.js Removes moment, converts flow to async/await, and updates HTTP dependency.
index.js Migrates S3 access to AWS SDK v3 and awaits downstream submission work to completion.
deploy.sh Marks legacy deploy script as deprecated with guidance to use the new workflow.
.gitignore Ignores Serverless build output (.serverless).
.github/workflows/test.yml Adds CI for tests and serverless template packaging validation.
.github/workflows/release.yml Updates release workflow Node version to 24.
.github/workflows/deploy-staging.yml Adds manual staging deploy workflow using osls + injected keys secret.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/submitlead.js Outdated
Comment thread lib/submitfeedback.js Outdated
Comment thread lib/submitfeedback.js
Comment thread lib/submitfeedback.js
Comment thread serverless.yml
johnrb2 and others added 6 commits June 29, 2026 12:53
Now that submitLead is async, drop the trailing callback parameter and resolve
with the response body on success (undefined when skipped or failed). The CLI
consumes the returned body via .then() to optionally open the lead in a
browser. Update tests to assert on the resolved value.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
- Guard the status-code checks in submitLead and submitFeedback against a
  missing response object (defensive; previously safe only via || short-circuit
  on err).
- Format the feedback lookback startDate from local date parts to preserve the
  original moment().format('YYYY-MM-DD') local-time behavior on the CLI (the
  Lambda runs in UTC regardless).
- Form-url-encode the feedback POST body with URLSearchParams so reasons with
  spaces or special characters are sent correctly; add a test that asserts the
  encoding round-trips.
- Document why excludeDevDependencies is intentionally false (the
  include-dependencies plugin already trims to required deps).

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Adopt leadconduit-api's ESLint setup, extending semistandard instead of
standard, with complexity capped at 10. Apply `eslint --fix` across the
repo and hand-fix the non-autofixable findings:

- refactor submitlead's fakeByLCName switch to a lookup map
- scope `complexity` disables on submitLead/submitFeedback (sequential
  flows with response guards)
- fix a latent implicit-global `fields` assignment in generateLead
- replace a top-level `return` in testfeedback with process.exit(3)
- drop dead `let url, fields, data, browser` in testlead

Enforce `npm run lint` in CI (test job).

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the bundled, gitignored demoConfig/keys.json with a configurable
resolver (lib/demokeys.js): DEMO_KEYS env (inline JSON) -> local file ->
SSM SecureString, with a "log in to AWS" fallback that prints actionable
guidance on a credentials error. Behavior is tunable via DEMO_KEYS,
DEMO_KEYS_FILE, DEMO_KEYS_SSM_PARAM, DEMO_KEYS_SOURCE and
DEMO_KEYS_DISABLE_SSM. The @aws-sdk/client-ssm dep is lazy-required so the
env/file paths never load it.

- index.js: demoFeedbacks awaits getDemoKeys()
- manualdemo.js: await both runners and surface resolver errors cleanly
- index.test.js: guard feedback processing so tests never hit live SSM

serverless.yml injects DEMO_KEYS as ${env:DEMO_KEYS, ssm:/test-sales-and-dev-leads/DEMO_KEYS, '{}'}.
The env fallback (over the plan's bare ssm form) lets `serverless package`
run credential-free, since osls errors on invalid/missing creds rather than
falling back on auth errors; a real deploy leaves DEMO_KEYS unset so the
value comes from SSM. Precedence mirrors lib/demokeys.js.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Switch deploy-staging.yml from static AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY
secrets to GitHub OIDC role assumption (aws-actions/configure-aws-credentials
assuming a dedicated least-privilege testlead-deployer role), and remove the
demoConfig/keys.json write step now that keys come from SSM. NPM_TOKEN remains
the only secret the workflow needs.

test.yml: drop the placeholder keys.json step (index.js no longer requires the
file) and set DEMO_KEYS='{}' so `serverless package` resolves credential-free.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Delete the retired deploy.sh (it bundled demoConfig/keys.json and could not
set the SSM-injected DEMO_KEYS env var). Rewrite the README keys/deployment
sections to cover the SSM SecureString (Doppler-sourced) keys, the
getDemoKeys() local resolution precedence and config knobs, OIDC deploy with
no AWS key secrets, the local osls break-glass path, and a correct keys.json
example shape (account name -> API key).

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
CodeQL flagged clear-text logging of sensitive information: the verbose
"query options" log stringified the request options, which include
auth.pass (the LeadConduit API key). Redact auth.pass before logging and
add a regression test asserting the key never appears in verbose output.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread lib/submitfeedback.js Fixed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 22 changed files in this pull request and generated 4 comments.

Comment thread lib/submitlead.js
Comment thread index.js
Comment thread .github/workflows/test.yml
Comment thread lib/testfeedback.js Outdated
johnrb2 and others added 2 commits June 30, 2026 11:58
- index.js: skip feedback (log) when no API key maps to the account instead
  of firing an unauthenticated request that would just 401/403; add a
  regression test.
- submitlead.js: log the resolved `body` in verbose mode rather than
  `response.body` (the request-capture shim doesn't set response.body, so
  verbose output was misleading).
- testfeedback.js: read commander's camelCase feedbackType/feedbackReason
  (the snake_case reads were always undefined, so the conversion default
  reason and unknown-type guard never fired) and handle the submitFeedback
  promise.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
The previous redaction overwrote auth.pass after spreading options, which is
safe at runtime but still trips CodeQL's js/clear-text-logging: its taint
analysis follows apiKey through `...options.auth` into the logged object and
doesn't model the override. Destructure auth out and rebuild it from only the
non-sensitive user field so the key never enters the logged value.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
@johnrb2
johnrb2 requested a review from Copilot June 30, 2026 18:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 22 changed files in this pull request and generated 1 comment.

Comment thread lib/testlead.js
johnrb2 and others added 4 commits June 30, 2026 12:29
submitLead returns a promise but the CLI only attached .then(), so a
rejection (e.g. a synchronous throw from the request library) would surface
as an unhandled rejection. Add a .catch() that logs and exits non-zero,
matching the testfeedback CLI.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Per DevOps guidance, stop injecting the demo API keys into the Lambda
environment (readable by anyone with function-view access) and drop SSM
Parameter Store. The Lambda now fetches the keys from AWS Secrets Manager at
runtime via its own function role; only the non-sensitive secret id is exposed
as an env var. Doppler remains the source of truth via a Doppler -> Secrets
Manager sync (project leadconduit-lambdas).

- package.json: swap @aws-sdk/client-ssm for @aws-sdk/client-secrets-manager.
- lib/demokeys.js: replace fromSsm() with fromSecretsManager() (GetSecretValue,
  parse SecretString as the account -> API key JSON map). Rename knobs to
  DEMO_KEYS_SECRET_ID / DEMO_KEYS_DISABLE_AWS and DEMO_KEYS_SOURCE=secretsmanager.
  Precedence is unchanged (env -> file -> AWS); the deployed Lambda has neither
  env nor file, so Secrets Manager is the operative runtime source.
- serverless.yml: drop the SSM-injected DEMO_KEYS env var; add the
  DEMO_KEYS_SECRET_ID env var and a scoped secretsmanager:GetSecretValue IAM
  statement on the secret ARN.
- workflows: packaging needs no deploy-time variable resolution now, so drop
  DEMO_KEYS='{}' from the validate job; fix the deploy-role comment.
- tests: mock SecretsManagerClient instead of SSMClient; rename the disable knob.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Rewrite the README keys, local-resolution, and deployment sections to describe
the runtime Secrets Manager fetch (Doppler -> SM sync, project
leadconduit-lambdas), the new resolution precedence and config knobs
(DEMO_KEYS_SECRET_ID, DEMO_KEYS_SOURCE=env|file|secretsmanager,
DEMO_KEYS_DISABLE_AWS), and the function role's secretsmanager:GetSecretValue
permission. The secret is never placed in the Lambda environment.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Serverless does not honor .gitignore, so a break-glass local `osls deploy` from
a checkout that still has demoConfig/keys.json would bundle those keys into the
Lambda zip. With the demo keys now resolved at runtime (env -> file -> Secrets
Manager) and no longer masked by a deploy-injected DEMO_KEYS env var, a bundled
file would take precedence over Secrets Manager and leak stale keys. Exclude
demoConfig/** from packaging so the deployed function always reads Secrets
Manager. Verified the artifact omits keys.json even when the file exists locally.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread index.js Fixed
Comment thread lib/demokeys.js Fixed
Comment thread lib/manualdemo.js Fixed
Sanitize every error/guidance string in lib/demokeys.js so no
process.env.DEMO_KEYS* read (secret id, file path, source selector) or
raw JSON.parse detail can reach a log/throw sink. This closes a narrow
real leak (the parse error could echo a fragment of a malformed
DEMO_KEYS value) and severs the taint CodeQL tracks into the index.js
and manualdemo.js log sinks, resolving the three js/clear-text-logging
alerts on PR #15.

Add a regression test asserting a malformed DEMO_KEYS value is never
echoed in the thrown error.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 22 changed files in this pull request and generated 4 comments.

Comment thread lib/demokeys.js Outdated
Comment thread lib/submitlead.js
Comment thread lib/submitfeedback.js
Comment thread lib/submitfeedback.js Outdated
Guard the two JSON.parse calls in submitFeedback so a 200/201 with a
non-JSON body (gateway error page, truncated response) logs and skips
that account instead of rejecting the promise and aborting the rest of
the per-minute feedback batch. The post-success parse is also guarded so
an unexpected body never marks an already-accepted (201) feedback as a
failed run.

Also fail with a clear message when a Secrets Manager secret has no
SecretString payload (e.g. stored as binary), rather than a misleading
"not valid JSON" error from parsing undefined.

Add tests covering both non-JSON feedback response paths.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 22 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

lib/testlead.js:35

  • commander.program.arguments() should describe only the arguments (not include the command name). Including testlead here makes the generated help usage read like testlead testlead <posting-url> ..., which is misleading.
program
  .arguments('testlead <posting-url> [fields...]')
  .option('-d, --data <parameters>', 'Defined data to send as-is. Provide in curl -d style ("a=42&foo=bar")', '')
  .option('-p, --probability <percentage>', 'Probability % of sending anything', 100)

Comment thread lib/testfeedback.js
johnrb2 and others added 2 commits July 10, 2026 16:19
testfeedback declared positional args (testfeedback <api_key>
<recipient_id>) that it never reads - the required inputs come via
-a/--apiKey and -i/--recipientId - and repeated the command name, so
--help implied an unsupported positional invocation. Drop the
.arguments() line so usage matches the documented options.

testlead's .arguments() likewise duplicated the command name; trim it to
'<posting-url> [fields...]' (its positionals are real and still consumed
in .action).

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
DevOps provisioned the demo keys as a Doppler -> Secrets Manager
single-secret sync (leadconduit-lambdas/staging_testlead) rather than
the placeholder name/shape the PR assumed. Update the secret id to
leadconduit-lambdas-staging-testlead-doppler in serverless.yml (env +
IAM ARN), the getDemoKeys default, tests, and README.

Single-secret sync stores the whole config as one JSON object with each
value serialized as a string, so the account -> API key map arrives
nested (and double-encoded) under a DEMO_KEYS key alongside DOPPLER_*
metadata. fromSecretsManager() now unwraps and parses that DEMO_KEYS
entry, falling back to treating the whole object as the bare map so a
hand-created secret or test stub still works. Add a test for the
Doppler-wrapped payload.

Refs sc-112520

Co-authored-by: Cursor <cursoragent@cursor.com>
@johnrb2
johnrb2 marked this pull request as ready for review July 15, 2026 16:00
@joshevensen-ap

Copy link
Copy Markdown

Code review — Josh Evensen — 2026-07-15

Engineer's assessment
It modernizes this repo

Jeremy's analysis
CI status: all checks passing (CodeQL, test, validate serverless template).

Reviewed high-risk files: index.js, lib/demokeys.js, lib/submitfeedback.js, lib/submitlead.js, serverless.yml, .github/workflows/deploy-staging.yml.

No blockers. Secret handling is solid — redaction in verbose logs, lazy-required Secrets Manager SDK, static (non-interpolated) error/guidance strings, and the deploy artifact excludes demoConfig/**. IAM permissions in serverless.yml are scoped tightly. OIDC deploy auth removes static AWS keys. Adjacent impact is contained — all consumers of the changed function signatures are internal to this repo and updated to the async API; the story confirms no external package consumers exist.

Two notes (non-blocking):

  1. index.js:6S3Client({}) no longer specifies an explicit region (the old code hardcoded us-west-1). This appears to fix a pre-existing region mismatch rather than introduce one (the bucket appears to live in us-east-1 per the README's S3 console link and serverless.yml's deploy region), but it's unverified without a live deploy.
  2. lib/submitlead.js / lib/submitfeedback.js — the request@activeprospect/request-capture/request swap is well covered by nock-based characterization tests, but nock intercepts at the HTTP layer and wouldn't catch a subtle option-handling difference in the wrapper itself. Worth a real staging smoke test post-merge (already implied by the PR's cutover plan).

Verdict: Approved with comments

High-risk files reviewed:

  • index.js
  • lib/demokeys.js
  • lib/submitfeedback.js
  • lib/submitlead.js
  • serverless.yml
  • .github/workflows/deploy-staging.yml

Files skipped (low risk):

  • .eslintrc.js, .eslintignore
  • .github/workflows/test.yml, .github/workflows/release.yml
  • .gitignore
  • README.md
  • deploy.sh (deletion)
  • lib/manualdemo.js, lib/testfeedback.js, lib/testlead.js
  • package.json, package-lock.json
  • test/*.js

@joshevensen-ap joshevensen-ap left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Node 24 / SDK v3 / OIDC-deploy modernization — clean async refactor with solid secret-handling and good characterization test coverage. Two non-blocking notes left inline (S3 region, request-capture smoke test).

Comment thread index.js
Comment thread lib/submitlead.js
@johnrb2
johnrb2 merged commit 381f809 into master Jul 15, 2026
4 checks passed
@johnrb2
johnrb2 deleted the chore/sc-112520-upgrade-testlead-node24 branch July 15, 2026 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants