Skip to content

feat(credentials): add managed credential groups - #6697

Merged
TheodoreSpeaks merged 18 commits into
stagingfrom
feat/slack-user-oauth
Aug 15, 2026
Merged

feat(credentials): add managed credential groups#6697
TheodoreSpeaks merged 18 commits into
stagingfrom
feat/slack-user-oauth

Conversation

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator

Summary

  • add enterprise Credential Groups settings, email enrollment, and managed credential storage
  • support Gmail, Google Calendar, and Slack custom-app OAuth through provider adapters
  • add a Credential Groups workflow block for listing credentials and people, sending invites, and listing groups
  • keep managed credentials hidden from Connected Accounts while allowing fail-closed credential ID execution

Type of Change

  • Feature

Testing

  • 264 targeted tests
  • bun run lint:check
  • bun run check:audits
  • bunx turbo run type-check --filter=sim --filter=@sim/auth
  • bun run check:migrations origin/staging

Checklist

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

@vercel

vercel Bot commented Aug 14, 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 15, 2026 1:39am

Request Review

@cursor

cursor Bot commented Aug 14, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Changes OAuth token issuance, scoped workflow delegation, and a public enrollment/OAuth surface alongside broad credential API filtering—core auth paths with large new application surface area.

Overview
Introduces Credential Groups so workspace admins can define account types (Gmail, Google Calendar, Slack custom app), invite people by email, and store connections as managed_oauth credentials that workflows use without exposing tokens in the UI.

Admin and enrollment surfaces include workspace settings (create/edit groups, invite/resend/revoke, Slack managed-user setup), invitation emails, a public /credential-groups/enroll/[token] flow with OAuth start/callback/complete routes, and internal workspace APIs behind shared error policies and invitation rate limits.

OAuth and credential APIs are tightened: managed_oauth rows are omitted from Connected Accounts–style listing and CRUD; the OAuth token route resolves managed IDs through workflow delegation (x-sim-managed-oauth-delegation), requires a tool ID, and applies trusted scope policies (e.g. Slack canonical scopes). Ordinary credentials ignore a stale delegation header.

Workflow integration adds a Credential Groups block and executor handler (list credentials/people/groups, send invites with rate limits) plus a new credential_group block type in the registry.

Gating: CREDENTIAL_GROUPS / enterprise billing drives settings nav, sidebar, and isCredentialGroupsAvailable; enrollment principals cannot create uploads.

Reviewed by Cursor Bugbot for commit c884334. Bugbot is set up for automated code reviews on this repo. Configure here.

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile-apps

@greptile-apps

greptile-apps Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds enterprise-managed credential groups, including invitation enrollment, custom-app OAuth adapters, managed credential execution, administrative settings, and workflow operations.

  • Introduces credential-group persistence, enrollment lifecycle management, provider-policy validation, and managed OAuth delegation.
  • Adds Gmail, Google Calendar, and Slack enrollment and execution paths.
  • Serializes grant persistence, resend, completion, and revocation transitions so terminal and credential-readiness state is revalidated before writes.
  • Adds API contracts, settings UI, workflow-block support, schema migration, and targeted regression tests.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
apps/sim/lib/credential-groups/oauth.ts Persists verified OAuth grants under lifecycle and policy locks while preserving completed reconnect state and rejecting revoked or stale-policy callbacks.
apps/sim/lib/credential-groups/enrollments.ts Implements invitation, resend, revocation, public enrollment, and completion transitions with compatible locks and conditional state updates.
apps/sim/app/api/auth/oauth/token/route.ts Adds fail-closed managed OAuth token resolution using scoped workflow delegation and trusted tool metadata.
apps/sim/lib/credentials/application/resolve-managed-oauth-token.ts Resolves managed credentials through the application-operation boundary with provider and scope enforcement.
packages/db/schema.ts Adds the credential-group and managed OAuth persistence model used by enrollment and delegated execution.
packages/db/migrations/0291_fuzzy_wong.sql Introduces the database structures and constraints required for managed credential groups.

Sequence Diagram

sequenceDiagram
  participant Admin
  participant API as Credential Group API
  participant DB as PostgreSQL
  participant User
  participant OAuth as OAuth Provider
  participant Exec as Workflow Executor

  Admin->>API: Create group and send invitation
  API->>DB: Persist enrollment and invitation token
  API-->>User: Enrollment email
  User->>API: Open invitation and start OAuth
  API->>OAuth: Authorization request
  OAuth-->>API: Callback and verified grant
  API->>DB: Lock lifecycle and revalidate policy
  API->>DB: Persist managed credential
  User->>API: Complete enrollment
  API->>DB: Lock and revalidate required credentials
  API->>DB: Mark enrollment completed
  Exec->>API: Request token with scoped delegation
  API->>DB: Authorize and resolve managed credential
  API-->>Exec: Access token
Loading

Reviews (15): Last reviewed commit: "fix(lint): use optional chain for pagina..." | Re-trigger Greptile

Comment thread apps/sim/lib/credential-groups/oauth.ts Outdated
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile-apps

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/api/auth/oauth/token/route.ts Outdated
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile-apps

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/credential-groups/enrollments.ts Outdated

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit caad5d0. Configure here.

Comment thread apps/sim/lib/credential-groups/enrollments.ts Fixed
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile-apps

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 4a2a6e7. Configure here.

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile-apps

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/credential-groups/enrollments.ts Outdated
Comment thread apps/sim/lib/credential-groups/enrollments.ts Outdated
Comment thread apps/sim/lib/credential-groups/enrollments.ts
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile-apps

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/api/credential-groups/oauth/[provider]/callback/route.ts Outdated
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c884334. Configure here.

@TheodoreSpeaks
TheodoreSpeaks merged commit 8fb571a into staging Aug 15, 2026
30 checks passed
@TheodoreSpeaks
TheodoreSpeaks deleted the feat/slack-user-oauth branch August 15, 2026 01:47
waleedlatif1 added a commit that referenced this pull request Aug 19, 2026
The four empty-state graphics and the shared frame add **+10 modules** to each
of the five routes that render them — measured against `origin/staging`, not
against the recorded baseline:

    files/[fileId]  1958 -> 1968
    files           1958 -> 1968
    knowledge       2167 -> 2177
    logs            1727 -> 1737
    tables          1817 -> 1827

The baseline itself was last recorded in #6697, and staging has drifted up to
+29 on tables since — inside the max(25, 2%) tolerance on its own, but close
enough that this +10 tipped it over. So the failure was the stale baseline
meeting a small real addition, not a heavy import. The other 29 entries move
only by that accumulated drift.

The graphics stay eagerly imported on purpose: an empty state is the first
thing a new workspace paints, and deferring ~4KB gzipped behind a chunk
request would trade a shared, already-fetched module for a visible pop on the
one screen where the product has to look like it works.
waleedlatif1 added a commit that referenced this pull request Aug 19, 2026
…d files (#6828)

* feat(resources): empty-state graphics for knowledge, tables, logs, files, skills

Four of the resource pages (knowledge, tables, logs, files) had no empty
state at all — `Resource.Table` painted column headers over a blank scroll
area and stopped there. Skills had a `: null` branch for zero data.

Adds a graphic per resource, drawn in the editor vignette's recipe: take the
product's own primitives, shrink them, strip the content to skeletons, and
let the composition bleed off the frame edges.

- Knowledge — a document fanning into the chunks it is embedded as, using the
  editor's 6px smooth-step connector language in --workflow-edge
- Tables — a sheet of cells running off two edges with one cell in an edit ring
- Logs — runs stacked newest-first, their trace spans staggered into a waterfall
- Files — a folder held open with one file still above its dashed landing slot
- Skills — a skill card opened far enough to show the tools bundled inside

`Resource.Table` gains a sanctioned `emptyState` slot rendered below the
column headers when `rows` is empty, so the chrome guarantee still holds.
Each page shows the graphic only for true zero-data — never for a search or
filter that matched nothing, never inside an empty subfolder, and (logs) never
before the first page of runs lands.

Also ports the shared `EmptyState` frame from the editor branch so this branch
stands alone, and adds a review-only /empty-states-preview gallery route.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* improvement(tables): redraw the empty-state graphic in the house grayscale

Matches the workflow editor's vignette and the landing feature graphics, which
between them use no brand colour at all — every one of them is built from
neutral tokens.

Two corrections:

- The blue edit ring is gone. Nothing in the reference graphics carries a hue,
  and it was the loudest element on the page.
- `--surface-4`/`--surface-5` are near-white in light mode (#f5f5f5/#f3f3f3), so
  skeleton geometry built on them dissolved on a white card. Bars now mix
  `--text-secondary` into transparent at graded strengths — a real mid-grey that
  inverts with the theme, which is the idiom the editor vignette already uses for
  the one bar it needs you to see.

Also drops the full-composition mask. The editor vignette keeps its block fully
opaque and fades only the connector strokes leaving the frame; masking
everything is what made the miniature read washed rather than deliberate. The
card is crisp now and the continuation is drawn the way a real table draws it —
an overflow fade at the edge the columns run off.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* improvement(tables): strip the empty-state graphic to ruled lines and a corner fade

Minimal pass. The card is gone — no border, no fill, no header shading, no type
squares. What is left is the grid itself: hairline rules in `--border-1`, ink
bars at two strengths, and the one cell held in an edit ring.

With no card fill the grid sits directly on the page, so it can dissolve into
the background instead of ending at a border. The fade is the landing page's own
idiom — two gradients intersected (`mask-composite: intersect`), crisp at the
top-left and gone through the bottom-right, the same construction
`workflow-graph-preview` uses.

Two placement notes:

- The grid is offset right of frame centre. A diagonal dissolve puts the visual
  mass toward its opaque corner, so centring the geometry would leave the
  graphic reading left of the copy beneath it.
- The selected cell sits in the quadrant the fade leaves fully opaque. A
  selection ring dissolving mid-stroke reads as a rendering fault, not a detail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* improvement(tables): make the selected cell opaque and a shade darker

The ring mixed `--text-secondary` into `transparent`, so the grid rules running
underneath showed through its own stroke. Mixing into `--bg` instead holds the
same apparent value while staying opaque, and still inverts with the theme.

Raised 32% -> 46% so it reads as chrome rather than more content, and added a
stacking context: neighbouring cells are later siblings, so their rules were
painting over the ring's right and bottom edges.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* improvement(tables): round the empty-state grid's crisp corner

6px on the top-left only — the one corner the fade leaves intact, and the same
radius the workflow editor's vignette uses. The other three dissolve, so there
is nothing there to round.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(knowledge,tables): redraw knowledge's empty state and add docs/create chips

Knowledge gets the same treatment tables just went through: no brand colour (the
`--brand-knowledge` accent is gone), no card chrome, ink mixed from
`--text-secondary`, and the landing page's intersected corner fade.

The graphic is a document and the chunks it is embedded as. Its fade is held
back further than the tables grid on both axes — the document has to stay whole
for the graphic to mean anything, so only the chunk grid may trail off. The
three chunks the edges actually land on are the only filled ones; filling the
whole first column left a chunk with no edge feeding it.

Both empty states now carry two chips in the frame's action slot — a docs link
and the create action, each running the same handler as the header's primary
chip and inheriting its disabled state.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* review(knowledge): three candidate depictions, and lead with the create chip

Chip order swapped on both empty states — the primary action reads first, the
docs link second.

Adds a review-only `knowledge-alternates.tsx` rendered in the preview gallery,
because the document-to-chunks graphic is not landing. Three directions:

- A. the embedding mesh — the landing hero's own knowledge-base panel already
  draws a base this way (`stage-kb.tsx`), so this is the house depiction rather
  than a new invention
- B. a stack of documents — the most literal reading, at the cost of colliding
  with what the files empty state wants to draw
- C. a query and the passages that answered it — depicts what a base is for,
  which is what the description copy actually promises

Delete this file and the gallery entries once one is chosen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(knowledge): draw the empty state as an isometric set of volumes

Replaces the document-to-chunks diagram, which read as a workflow graph rather
than as a knowledge base.

Built on the landing page's iso-illustration recipe rather than a new one:
`ISO_STROKE` contours (`--text-subtle` mixed toward `--text-muted`) at the shared
3.2 stroke width, faces filled from the three-tier surface ramp brightest-on-top,
round caps and joins. Geometry is authored in a large unit space so that 3.2
lands as a hairline once scaled to empty-state size — the same reason the landing
marks draw 3.2 into a ~526-unit viewBox.

The projection and faces are computed rather than hand-authored as path data, so
the volumes stay coherent when the geometry is retuned.

No corner fade here. The fade belongs to repeating structures that mean the same
thing cropped — the tables grid keeps its meaning with two columns or four. A
discrete object does not, which is also why the workflow editor's vignette keeps
its block fully opaque.

Drops the three candidate depictions now that the direction is settled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* improvement(knowledge): brand the front volume instead of laying a page beside it

Drops the loose page on the ground and puts the knowledge-base mark on the front
volume's cover — the same `Database` glyph the sidebar and the page header use,
so the empty state names its own resource.

The mark is laid into the cover's plane rather than drawn over it. The cover is
the face at max x, spanned by the volume's depth across and its height up;
walking those two edges gives the face's basis vectors in projected space, and an
affine matrix built from them maps flat artwork into the face. So the glyph
skews with the isometric, and because both vectors derive from the box, retuning
the volumes carries the mark with them instead of stranding hand-fitted path data.

Its stroke is pre-divided by the same factor the matrix scales by, so the glyph's
contours land at the volumes' weight rather than four times it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* improvement(knowledge): bore through the front volume, and fade the set back

Replaces the mark on the cover with a hole through it.

The bore is authored as a plain circle in the cover's own plane and skewed into
an ellipse by the face matrix. Its far mouth is the same circle stepped back
through the volume: boring straight back is a world step of `-w` along x, and
solving the cover-plane matrix for the local offset that produces it gives
`(+w, -w)`. The sliver of near mouth the far mouth fails to cover is exactly the
wall you see down the hole, so the depth falls out of the geometry rather than
being drawn by hand.

Down the hole the near mouth is floored in a tone darker than any outer face —
the wall turns away from the light — and the far mouth is painted in the cover
tone of the volume standing behind it, because looking through a hole in the
front volume lands on that volume's face, not on the page.

Corners stay square. Rounding was tried and reverted: rounding each face
separately notches every corner where three faces meet, and rounding the
silhouette instead cost a clip per volume for a softness the set did not want.

The tables grid's corner fade is applied along the other diagonal. There it
dissolves toward the bottom-right because a grid keeps its meaning cropped; here
the set recedes up and to the left and the front volume carries the bore, so
anchoring at the bottom-right eats into the back of the stack and reads as more
volumes behind.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(resources): logs and files graphics, and settle the set as one collection

Logs is an activity feed — newest run lifted onto its own card, older ones
settling behind it. The relative stamps are the only literal text in any of these
graphics; everything else stays skeleton, so nothing here has to be translated or
kept true.

Files is a folder with sheets standing proud of its front panel. Depth comes from
the surface ramp rather than shadow, which would need separate light and dark
recipes where the ramp inverts on its own. The tab's diagonal is filleted at both
ends and every outer corner shares one radius — mixing radii, or running the
diagonal into square junctions, made the corners fight at this size.

Consistency pass across the set:

- Titles are the resource name alone. "No tables yet" earned nothing the
  description does not already say.
- The knowledge mark is mirrored so its bore faces left. Rebuilt on the geometry
  rather than flipped, since a flip would have put the shading on the wrong side.
  Its contours are thinned and mixed toward `--border-1`: the landing marks are
  the focal art of their section, but this one sits beside a ruled grid whose
  lines are 1px, and full-weight contours read as ink next to it.
- The logs feed is sized to the same ~148px footprint as the rest. The frame
  centres graphic and copy together, so a taller graphic pushes its title out of
  line with the others' and the set stops reading as one thing.
- Every empty state carries its create action and a docs link, each running the
  same handler as the header's primary chip.
- Fades run whichever way the subject recedes: the tables grid to the
  bottom-right, the knowledge set up and right, the logs feed down, the folder
  up.

Fixes a duplicate React key in the knowledge mark — the volumes stack along y
now, so keying on `box.x` gave every one of them `0`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* revert(skills): drop the skills empty state

Removed at request. The skills list goes back to rendering nothing for zero data,
which is what it did before this branch.

Takes `vignette.tsx` with it — the shared stage and skeleton bar were left over
from the first pass, and skills was the last thing still importing them once the
other four graphics were redrawn.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* cleanup(resources): fix empty-state flashes and share the iso ramp

Drops the review-only preview route and gallery, which the branch always
meant to delete before merging.

Three ways the zero-data graphic painted over a workspace that has content:

- The gate read the instant URL search term while `rows` is filtered by the
  debounced one, so clearing a search that matched nothing showed the full
  "you have nothing yet" state for one debounce window.
- Nothing gated on the list still loading. Knowledge and tables hydrate from
  a server prefetch that is allowed to seed nothing, and the files list
  deliberately seeds nothing above 300 rows — so the emptiest-looking screen
  was shown to the fullest workspaces.
- The filters are part of the query key and every list keeps the previous
  key's data, so `isLoading` is false across a filter change. Only the
  placeholder gate suppresses the graphic during that refetch.

Also folds the re-declared isometric fills and stroke back onto the shared
`iso-illustration-style` source they were copied from, so a change to the iso
ramp reaches this mark too; only the stroke width still diverges. The static
face paths move to module scope, the bore interior becomes a named component
so its note is TSDoc rather than a JSX comment, and the four identical docs
chips become one.

* simplify(resources): reuse the iso recipe and let the frame own its layout

Hides the empty-state graphic behind `error` as well. A failed load also
leaves `rows` empty, and inviting someone to create their first item is the
wrong answer to a request that did not complete — all four pages only logged
the error, so the zero-data copy was what a failed load actually rendered.

`iso-illustration-style` moves out of the landing route group to
`components/iso/`. Importing it from a workspace route was the only
workspace-to-landing edge in the app, one directory away from an `iso-marks`
barrel that pulls ~10KB gzipped of illustration components — a hazard for
whoever needs the second constant. The contour recipe is now shared too:
`createIsoLineProps` takes an optional stroke width, so the knowledge mark
stops re-declaring it and only its weight diverges.

`EmptyState` owns the action row's layout, so the three pages with two chips
drop their wrapper div and every empty state's chips sit identically. Its
unused `className` prop goes with them. Also drops the `height` prop that had
one caller passing its default, and the `CORNER` constant that promised
single-sourcing the path's four bare literals did not honour.

* simplify(resources): decide list emptiness in one place

"This list holds nothing" was derived in four pages, each with the same seven
clauses under the same nine-line comment. Adding the `error` gate one commit
ago took four identical edits, and the skills empty state that was reverted
off this branch would have made it five copies.

`isResourceListEmpty` now owns the rule and the reasoning behind each gate.
Logs omits the folder argument because it has no folder navigation; the other
three pass theirs.

`Resource.Table` also wraps the slot in its own growth box, so the empty state
centres because the table says so rather than because the node handed to it
happened to carry `flex-1`.

* chore(audits): re-record the page module-graph baseline

The four empty-state graphics and the shared frame add **+10 modules** to each
of the five routes that render them — measured against `origin/staging`, not
against the recorded baseline:

    files/[fileId]  1958 -> 1968
    files           1958 -> 1968
    knowledge       2167 -> 2177
    logs            1727 -> 1737
    tables          1817 -> 1827

The baseline itself was last recorded in #6697, and staging has drifted up to
+29 on tables since — inside the max(25, 2%) tolerance on its own, but close
enough that this +10 tipped it over. So the failure was the stale baseline
meeting a small real addition, not a heavy import. The other 29 entries move
only by that accumulated drift.

The graphics stay eagerly imported on purpose: an empty state is the first
thing a new workspace paints, and deferring ~4KB gzipped behind a chunk
request would trade a shared, already-fetched module for a visible pop on the
one screen where the product has to look like it works.

* fix(resources): hold the empty state until folders resolve

Folder rows share the list with resource rows, so a workspace whose only
contents are folders has an empty `rows` until the folder tree lands — and got
the "create your first item" graphic in the gap. The resource list's own
loading gates never covered it because the folder tree is a separate query.

`useFolderNavigation` already exposes `foldersResolved` (`isSuccess &&
!isPlaceholderData`) for exactly this hazard — it guards the ancestry index
against evicting a folder id it has not loaded yet. Knowledge and tables pass
it straight through; files reads the same two flags off
`useWorkspaceFileFolders`, which it calls directly. Logs omits it, as it has
no folders.

---------

Co-authored-by: andresdjasso <andresdjasso@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Waleed Latif <walif6@gmail.com>
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.

2 participants