Skip to content

fix(selectors): resolve shared environment references server-side - #7095

Open
BillLeoutsakosvl346 wants to merge 10 commits into
stagingfrom
fix/server-resolved-selector-context
Open

fix(selectors): resolve shared environment references server-side#7095
BillLeoutsakosvl346 wants to merge 10 commits into
stagingfrom
fix/server-resolved-selector-context

Conversation

@BillLeoutsakosvl346

@BillLeoutsakosvl346 BillLeoutsakosvl346 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the shared, authorized infrastructure for dependent selectors whose context may contain environment-secret references. This PR contains no Jira, Confluence, JSM, Slack, CloudWatch, or IMAP selector migration; those are independent sibling PRs stacked on this branch.

The browser carries literals or opaque exact {{KEY}} references only. Authorized resolution happens server-side, and resolved shared-secret plaintext never enters selector query keys, responses, or client state.

Shared infrastructure in this PR:

  • serverResolvedContextFields metadata and exact-reference preservation
  • Principal-based workflow/credential authorization
  • active readable workflow scope, delegated executor scope, and session-only workflowless credential scope
  • canonical workspace derivation and workflow/credential workspace agreement
  • effective environment resolution with workspace-over-personal precedence and existing shared-secret membership filtering
  • credential provider matching before token or service-account secret access
  • cache partitioning by workspace, workflow, and opaque dependency revision
  • knowledge-connector cache revisions derived only from each selector server-resolved dependency fields, so unrelated connector edits do not churn selector identity
  • successful personal/workspace environment mutations refresh their environment query first, then invalidate primary selectors, canvas detail labels, and workflow-search selector caches without changing opaque query keys
  • successful Copilot personal-secret updates refresh both personal and workspace-environment queries; Copilot workspace-secret updates refresh the canonical workspace query before the same selector invalidation
  • complete selector-query reset during user-data reset
  • shared Atlassian credential binding, sanitized discovery mode, provider-error mapping, and provider-owned selector contract map composition used by the Jira/Confluence and JSM children

Slack report: https://sim-ai.slack.com/archives/C093DF8MA21/p1786822370070229

Security invariant

  • Route adapters authenticate before parsing and reuse the authenticated session or executor principal.
  • Workflow selectors require an active workflow the principal may read.
  • Workflowless selectors require a session principal and an authorized credential; direct-secret selectors still require a workflow.
  • Credential-backed requests use existing credential-use authorization and enforce workflow/credential workspace agreement.
  • Provider compatibility is checked before token refresh or service-account secret access.
  • Only exact whole-value references resolve; missing and inaccessible references return the same sanitized error.
  • Resolved values never return to the browser or enter logs/query keys.
  • Atlassian provider response bodies are neither returned nor logged; sanitized non-2xx bodies are best-effort cancelled without inspection.
  • Personal environment writes immediately evict the server's effective-environment cache; failed browser mutations do not invalidate selector caches.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: ___________

Focused coverage

  • Literal passthrough; exact personal/shared references; workspace precedence; missing/inaccessible equivalence; exact-reference-only behavior.
  • Active/archived workflow authorization, delegated scope, workflowless credential scope, workflow/credential workspace agreement, and direct-secret workflow enforcement.
  • OAuth and Atlassian service-account provider binding plus mismatch denial before secret/provider access.
  • Direct database-adapter coverage for OAuth, Atlassian and Slack service accounts, incompatible providers, and owner-scoped legacy accounts.
  • One real QueryClient suite for opaque revisions, workspace/workflow separation, plaintext exclusion, and unchanged legacy keys.
  • Real QueryClient mutation coverage for personal saves, workspace upserts/removals, all four selector cache families, unrelated-query isolation, and failed-mutation behavior.
  • Immediate personal effective-environment cache eviction after the database write.
  • User-data reset removes the entire selector query namespace.
  • Server adapter authentication success/failure and the sim:selectors delegation audience.
  • Exact public provider-error mappings without incidental implementation assertions.

Verification

Verified after rebuilding on the latest origin/staging:

  • Focused shared Vitest: 15 files, 95 tests passed.
  • Provider-focused combined selector suites: 15 files, 75 tests passed.
  • Full root suite on the final combined head: 19/19 tasks; app 2,368 files passed, 3 skipped; 34,842 tests passed, 46 skipped.
  • App and root type-checks passed.
  • Root lint:check and format:check passed. Lint reports one unrelated existing unused-suppression warning in shell-layout.test.ts.
  • Strict API validation passed: 1,213/1,213 routes Zod-backed.
  • Client-boundary and React Query audits passed.
  • git diff --check passed.

The final review follow-up adds behavior-level regressions for connector dependency-scoped cache identity, Copilot personal/workspace secret-update invalidation, and sanitized Atlassian response cancellation without duplicating provider matrices.

Combined browser acceptance

  • Jira, Confluence, JSM, CloudWatch, Slack, and IMAP selector forms preserved literals and exact personal/shared references across the browser boundary.
  • Existing Slack OAuth loaded real channel options in both a workflow and a workflowless knowledge connector.
  • Changing between two deterministic invalid Slack direct tokens produced separate requests; an exact runtime block reference produced no selector request.
  • CloudWatch and IMAP deterministic invalid inputs reached their dedicated selector routes and returned sanitized failures without echoing secrets.
  • No selector-specific console warnings appeared and no plaintext secret values appeared in keys, responses, or server logs.
  • Disposable workflow and knowledge-base data were deleted afterward; the existing Slack workflow was restored.
  • Freshness follow-up smoke sent an exact missing Jira domain reference to the dedicated Project/Issue routes and surfaced only the sanitized Credential not found response. The temporary blocks and values were undone, and reload confirmed the workflow returned to its original Start-only state.
  • The exact mounted same-reference mutation is covered by the real QueryClient regression because this local account cannot edit Secrets through the UI.

Intentional scope

  • Exact whole-value references only; embedded interpolation is unchanged.
  • Runtime tool routes, block definitions, database schemas, runtime environment resolution, and chained resource-ID selectors are unchanged.
  • Provider selector migrations remain in independent child PRs.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No selector-specific warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Stacked children

Each child is a draft sibling based directly on this PR's tip. Review each diff against fix/server-resolved-selector-context; do not merge a child until #7095 lands and that child is rebased and retargeted to staging.

@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Aug 27, 2026 12:31am

Request Review

@BillLeoutsakosvl346
BillLeoutsakosvl346 force-pushed the fix/server-resolved-selector-context branch from 2402184 to 2434652 Compare August 26, 2026 17:50
@BillLeoutsakosvl346
BillLeoutsakosvl346 force-pushed the fix/server-resolved-selector-context branch from 2434652 to ad5a672 Compare August 26, 2026 19:03
@BillLeoutsakosvl346
BillLeoutsakosvl346 force-pushed the fix/server-resolved-selector-context branch from ad5a672 to 0b8f18c Compare August 26, 2026 20:21
@BillLeoutsakosvl346
BillLeoutsakosvl346 force-pushed the fix/server-resolved-selector-context branch from fafded6 to 0486f57 Compare August 26, 2026 22:25
@BillLeoutsakosvl346
BillLeoutsakosvl346 force-pushed the fix/server-resolved-selector-context branch from 0486f57 to 17726cb Compare August 26, 2026 23:28
@BillLeoutsakosvl346
BillLeoutsakosvl346 marked this pull request as ready for review August 26, 2026 23:32
@greptile-apps

greptile-apps Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds shared infrastructure for resolving authorized selector environment references server-side while keeping secret values out of browser query keys and state.

  • Adds principal-, workflow-, workspace-, and credential-aware selector context authorization.
  • Introduces opaque browser cache partitions and environment-mutation invalidation across selector query families.
  • Adds sanitized Atlassian credential discovery, provider binding, and provider-error handling.
  • Clears server-state queries during user-data reset and refreshes the effective environment cache after personal environment writes.

Confidence Score: 5/5

The PR appears safe to merge; no concrete changed-code failure remains reachable in the reviewed changes.

The authorization path derives canonical scope before environment resolution, credential-backed requests enforce workspace agreement, and browser caches are partitioned and invalidated without exposing resolved secret values.

Important Files Changed

Filename Overview
apps/sim/lib/selectors/application/resolve-authorized-context.ts Adds the central authorized application operation for canonical workspace derivation, credential checks, delegated scope enforcement, and exact environment-reference resolution.
apps/sim/lib/selectors/application/credential-provider.ts Adds provider compatibility checks before credential secret access; the helper is not yet reached by a production caller in this changeset.
apps/sim/hooks/selectors/context-resolution.ts Preserves server-resolved references and creates stable opaque cache revisions without exposing raw dependency values in query keys.
apps/sim/hooks/selectors/use-selector-query.ts Partitions opted-in selector caches by canonical workspace, workflow, and opaque dependency scope while retaining legacy keys for other selectors.
apps/sim/hooks/queries/environment.ts Refreshes environment queries first and then invalidates selector-dependent caches after successful environment mutations.
apps/sim/lib/atlassian/discovery.ts Adds a sanitized discovery mode that excludes provider-controlled response bodies from errors and separates sanitized and standard cache entries.
apps/sim/stores/index.ts Expands user-data reset from environment-only removal to clearing the complete React Query cache.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant Route as Selector route
    participant Auth as Principal authorization
    participant Env as Effective environment
    participant Provider
    Browser->>Route: "Opaque context with exact {{KEY}} reference"
    Route->>Auth: Authenticate principal and authorize workflow/credential
    Auth-->>Route: Canonical workspace and credential scope
    Route->>Env: Resolve authorized exact references
    Env-->>Route: Resolved server-only context
    Route->>Provider: Fetch selector options
    Provider-->>Route: Provider result
    Route-->>Browser: Sanitized selector options
    Note over Browser: Query key contains workspace/workflow/opaque revision, not plaintext
Loading

Reviews (1): Last reviewed commit: "fix(selectors): refresh caches after env..." | Re-trigger Greptile

@cubic-dev-ai cubic-dev-ai Bot 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.

All reported issues were addressed across 37 files

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

Comment thread apps/sim/lib/atlassian/discovery.ts
@BillLeoutsakosvl346
BillLeoutsakosvl346 force-pushed the fix/server-resolved-selector-context branch from 17726cb to aeaa7ec Compare August 27, 2026 00:17

@cubic-dev-ai cubic-dev-ai Bot 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.

All reported issues were addressed across 40 files

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

Comment thread apps/sim/app/workspace/[workspaceId]/home/hooks/stream/handle-tool-event.ts Outdated
@BillLeoutsakosvl346

Copy link
Copy Markdown
Contributor Author

Addressed the latest Cubic P2 in eb0d42e. Successful personal Copilot secret updates now invalidate both the personal environment query and the workspace-environment prefix before invalidating the four selector cache namespaces, matching the regular personal-save mutation. The focused stream suite (13 tests), app/root type-checks, lint, format, strict API validation, client-boundary, React Query audit, and diff check all pass on the rebuilt combined stack.

@BillLeoutsakosvl346

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 27, 2026

Copy link
Copy Markdown

@cubic-dev-ai review this PR

@BillLeoutsakosvl346 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot 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.

No issues found across 40 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

@BillLeoutsakosvl346

Copy link
Copy Markdown
Contributor Author

Server-resolved selectors: visual architecture guide

These diagrams summarize the original failure, the new secure request path, cache behavior, affected integrations, file ownership, and the stacked merge sequence.

1. Before: browser-dependent environment resolution

flowchart TB
    INPUT["Block field<br/>Domain = JIRA_DOMAIN reference"]
    SETUP["useSelectorSetup tries to build<br/>literal selector context"]
    CACHE{"Can the browser read<br/>the environment value?"}

    PERSONAL["Personal variable<br/>resolved to plaintext"]
    SHARED["Workspace/shared secret<br/>cannot be viewed by browser"]

    GOOD_REQUEST["API receives literal domain"]
    BAD_REQUEST["API receives unresolved reference"]
    SUCCESS["Projects/pages load"]
    FAILURE["Validation or provider request fails"]

    INPUT --> SETUP --> CACHE

    CACHE -->|"Yes"| PERSONAL
    PERSONAL --> GOOD_REQUEST --> SUCCESS

    CACHE -->|"No"| SHARED
    SHARED --> BAD_REQUEST --> FAILURE
Loading

The original implementation only worked when the browser already possessed the value. This is why personal variables could work while shared secrets failed.

2. After: authorized server-side resolution

flowchart LR
    subgraph Browser
        INPUT["Block field<br/>Domain = JIRA_DOMAIN reference"]
        REQUEST["Preserve reference<br/>and send authorized scope"]
        RESULT["Normalized options<br/>IDs and labels only"]

        INPUT --> REQUEST
    end

    subgraph Server
        AUTH["Authenticate principal"]
        SCOPE["Authorize workflow,<br/>credential and provider"]
        RESOLVE["Resolve reference<br/>server-side"]

        AUTH --> SCOPE --> RESOLVE
    end

    subgraph ExternalProvider["External provider"]
        API["Provider receives<br/>literal domain"]
    end

    REQUEST --> AUTH
    RESOLVE --> API
    API --> RESULT
Loading

The browser now carries references; the authorized server owns plaintext resolution.

3. Complete secure request flow

sequenceDiagram
    actor User
    participant UI as Selector UI
    participant Cache as React Query
    participant Route as Selector API
    participant Auth as Authorized resolver
    participant Env as Effective environment
    participant Provider as Jira / Slack / AWS / IMAP

    User->>UI: Open dependent selector
    UI->>UI: Preserve literal or exact KEY reference
    UI->>Cache: Query with workspace + workflow + opaque revision
    Cache->>Route: Raw reference + authorized scope

    Route->>Auth: Authenticate session/executor
    Auth->>Auth: Load canonical workflow/workspace
    Auth->>Auth: Authorize credential use
    Auth->>Auth: Verify credential provider compatibility
    Auth->>Env: Resolve allowlisted exact references

    Env-->>Auth: Decrypted values in server memory
    Auth-->>Route: Authorized principal + resolved context
    Route->>Provider: Request using resolved values
    Provider-->>Route: Provider resources
    Route-->>Cache: Normalized options or sanitized error
    Cache-->>UI: IDs and labels

    Note over UI,Cache: No shared-secret plaintext
    Note over Auth,Provider: Plaintext exists only server-side
Loading

4. Permission and secret boundary

flowchart LR
    subgraph Browser["Browser boundary"]
      UI["Selector UI"]
      REF["Raw exact reference"]
      KEY["Opaque query-key revision"]
      OPTIONS["Normalized IDs and labels"]
    end

    subgraph Server["Authorized server boundary"]
      PRINCIPAL["Authenticated Principal"]
      WORKFLOW["Canonical workflow/workspace"]
      CRED["Credential-use authorization"]
      BIND["Provider binding"]
      RESOLVE["Effective environment resolution"]
      PLAIN["Resolved plaintext"]
    end

    subgraph SecretStores["Secret sources"]
      SHARED["Workspace/shared secret<br/>may use does not mean may view"]
      PERSONAL["Viewer personal variable"]
    end

    subgraph External["External provider"]
      API["Provider API"]
    end

    UI --> REF
    REF --> PRINCIPAL
    KEY --> PRINCIPAL
    PRINCIPAL --> WORKFLOW --> CRED --> BIND --> RESOLVE

    SHARED --> RESOLVE
    PERSONAL --> RESOLVE
    RESOLVE --> PLAIN --> API
    API --> OPTIONS --> UI

    SHARED -. "never returned" .-> UI
    PLAIN -. "never enters query keys" .-> KEY
Loading

Key invariants:

  • Workspace/shared values retain workspace-over-personal precedence.
  • Missing and inaccessible references produce the same sanitized failure.
  • Credential-provider mismatch stops before refresh or provider access.
  • Runtime block-output references remain ineligible.
  • Exact whole-value references are supported; embedded interpolation is unchanged.

5. Cache privacy and freshness

flowchart TB
    RAW["Raw selector dependencies<br/>kept privately in component memory"]
    REGISTRY["Opaque revision registry"]
    RANDOM["Random revision<br/>for this component lifetime"]

    WORKSPACE["Workspace ID"]
    WORKFLOW["Workflow ID"]
    SELECTOR["Selector name"]

    KEY["React Query key<br/>selector + workspace + workflow + opaque revision"]
    REQUEST["Selector request"]

    RAW --> REGISTRY --> RANDOM --> KEY
    WORKSPACE --> KEY
    WORKFLOW --> KEY
    SELECTOR --> KEY
    KEY --> REQUEST

    subgraph Mutation["When an environment value changes"]
      SAVE["Personal/workspace/Copilot mutation succeeds"]
      REFRESH["Refresh environment query"]
      INVALIDATE["Invalidate selector-bearing namespaces"]
      REFETCH["Mounted selectors refetch"]
      SAVE --> REFRESH --> INVALIDATE --> REFETCH
    end

    INVALIDATE --> REQUEST

    subgraph Namespaces["Invalidated namespaces"]
      PRIMARY["Primary selector options"]
      DETAIL["Canvas dynamic-detail labels"]
      SEARCH["Workflow-search details"]
      REPLACE["Workflow replacement options"]
    end

    INVALIDATE --> PRIMARY
    INVALIDATE --> DETAIL
    INVALIDATE --> SEARCH
    INVALIDATE --> REPLACE

    RESET["Sign-out / identity reset"] --> CLEAR["Clear entire QueryClient"]
    CLEAR --> PRIMARY
    CLEAR --> DETAIL
    CLEAR --> SEARCH
    CLEAR --> REPLACE
Loading

The opaque revision handles changed raw dependencies. Mutation invalidation handles an unchanged reference whose underlying value changes.

6. Why these integrations were affected

flowchart TB
    START["Does the selector depend on another configured field?"]
    SECRET{"Can that dependency<br/>contain a secret reference?"}
    LITERAL{"Does the provider request<br/>need the resolved literal?"}
    AFFECTED["Affected by the original bug"]
    SAFE["Not directly affected"]

    START -->|"No"| SAFE
    START -->|"Yes"| SECRET
    SECRET -->|"No: ordinary resource ID"| SAFE
    SECRET -->|"Yes"| LITERAL
    LITERAL -->|"No: opaque credential ID<br/>already resolved server-side"| SAFE
    LITERAL -->|"Yes"| AFFECTED

    AFFECTED --> JIRA["Jira<br/>Domain → Projects → Issues"]
    AFFECTED --> CONF["Confluence<br/>Domain → Spaces → Pages"]
    AFFECTED --> JSM["JSM<br/>Domain → Service desks → Request types"]
    AFFECTED --> CW["CloudWatch<br/>AWS config → Log groups → Streams"]
    AFFECTED --> SLACK["Slack<br/>Token/credential → Channels and users"]
    AFFECTED --> IMAP["IMAP<br/>Connection fields → Mailboxes"]
Loading

Most selectors were already safe because they send opaque credential IDs, depend only on non-secret resource IDs, or do not carry secret-bearing selector context.

7. Shared infrastructure and provider-specific code

flowchart LR
    subgraph BrowserInfra["Shared browser infrastructure — PR #7095"]
      TYPES["selectors/types.ts<br/>serverResolvedContextFields"]
      SETUP["use-selector-setup.ts<br/>preserve references"]
      CONTEXT["context-resolution.ts<br/>opaque revision registry"]
      KEYS["query-keys.ts<br/>scoped private keys"]
      QUERY["use-selector-query.ts<br/>fetch options"]
      INVALIDATE["cache-invalidation.ts<br/>freshness after mutations"]

      TYPES --> SETUP --> CONTEXT --> KEYS --> QUERY
      INVALIDATE --> QUERY
    end

    subgraph ServerInfra["Shared server infrastructure — PR #7095"]
      ADAPTER["server/resolve-authorized-context.ts<br/>authenticate adapter"]
      USECASE["application/resolve-authorized-context.ts<br/>canonical authorization"]
      PROVIDER["credential-provider.ts<br/>provider compatibility"]
      ENV["getEffectiveDecryptedEnv<br/>server-only resolution"]
      ERRORS["provider-errors.ts<br/>sanitized failures"]

      ADAPTER --> USECASE
      USECASE --> PROVIDER
      USECASE --> ENV
      PROVIDER --> ERRORS
      ENV --> ERRORS
    end

    BrowserInfra --> ServerInfra

    ServerInfra --> ATL["#7122 Jira + Confluence<br/>routes, selectors, contracts"]
    ServerInfra --> JSMPR["#7114 JSM<br/>routes, selectors, contracts"]
    ServerInfra --> SLACKPR["#7115 Slack<br/>credential resolver and routes"]
    ServerInfra --> CWPR["#7116 CloudWatch<br/>dedicated selector routes"]
    ServerInfra --> IMAPPR["#7117 IMAP<br/>resolved connection route"]

    ATLHELP["Shared Atlassian helpers<br/>atlassian-credential.ts<br/>atlassian/discovery.ts"] --> ATL
    ATLHELP --> JSMPR
Loading

8. Pull-request stack

flowchart TB
    STAGING["staging"]
    PR1["#7095<br/>Shared secure selector infrastructure<br/>+ reusable Atlassian support"]

    ATL["#7122<br/>Jira + Confluence"]
    JSM["#7114<br/>Jira Service Management"]
    SLACK["#7115<br/>Slack"]
    CW["#7116<br/>CloudWatch"]
    IMAP["#7117<br/>IMAP"]

    STAGING --> PR1
    PR1 --> ATL
    PR1 --> JSM
    PR1 --> SLACK
    PR1 --> CW
    PR1 --> IMAP

    ATL -.-|"no sibling file overlap"| JSM
    JSM -.-|"no sibling file overlap"| SLACK
    SLACK -.-|"no sibling file overlap"| CW
    CW -.-|"no sibling file overlap"| IMAP
Loading

9. Files grouped by responsibility

flowchart TB
    subgraph PR1["#7095 — 40 files"]
      P1A["Browser context and cache<br/>use-selector-setup.ts<br/>context-resolution.ts<br/>query-keys.ts<br/>use-selector-query.ts"]
      P1B["Authorization<br/>resolve-authorized-context.ts<br/>credential-provider.ts"]
      P1C["Freshness and teardown<br/>environment.ts<br/>cache-invalidation.ts<br/>stores/index.ts"]
      P1D["Shared Atlassian safety<br/>atlassian-credential.ts<br/>atlassian/discovery.ts<br/>provider-errors.ts"]
      P1E["15 focused test files"]
    end

    subgraph ATLPR["#7122 — 13 files"]
      A1["Jira routes<br/>projects/route.ts<br/>issues/route.ts"]
      A2["Confluence routes<br/>selector-spaces<br/>selector-pages<br/>selector-page"]
      A3["Jira/Confluence selectors and contracts"]
      A4["3 focused test files"]
    end

    subgraph JSMPR["#7114 — 6 files"]
      J1["Service Desk route"]
      J2["Request Type route"]
      J3["JSM selector + contract"]
      J4["2 focused test files"]
    end

    subgraph SLACKPR["#7115 — 12 files"]
      S1["slack-credential.ts"]
      S2["Channels and Users routes"]
      S3["Raw dependency transport"]
      S4["Slack selectors and contract"]
      S5["3 focused test files"]
    end

    subgraph CWPR["#7116 — 10 files"]
      C1["Dedicated group/stream routes"]
      C2["Reusable AWS listing utilities"]
      C3["CloudWatch selector + contracts"]
      C4["4 focused test files"]
    end

    subgraph IMAPPR["#7117 — 7 files"]
      I1["Mailbox route"]
      I2["Wire/resolved contracts"]
      I3["Safe host validation"]
      I4["3 focused test files"]
    end

    PR1 --> ATLPR
    PR1 --> JSMPR
    PR1 --> SLACKPR
    PR1 --> CWPR
    PR1 --> IMAPPR
Loading

10. Landing sequence

flowchart LR
    R1["Merge #7095"]
    R2["Record old PR1 tip"]
    R3["Rebase each child<br/>onto staging"]
    R4["Retarget child PRs<br/>to staging"]
    R5["Run focused tests<br/>and confirm independence"]
    R6["Merge children<br/>in any order"]

    R1 --> R2 --> R3 --> R4 --> R5 --> R6
Loading

Core model

flowchart LR
    BROWSER["Browser<br/>reference only"]
    SERVER["Authorized server<br/>plaintext allowed"]
    PROVIDER["External provider"]
    RESULT["Browser<br/>options only"]

    BROWSER --> SERVER --> PROVIDER --> SERVER --> RESULT
Loading

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.

1 participant