feat(website): improve Envilder discoverability - #474
Conversation
Treat WebP files as static assets so CloudFront does not redirect poster requests to directory URLs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Remove unsupported Marketplace and GCP examples. Correct rotation and audit semantics. Keep public docs synchronized with current CLI and Action behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
Warning Review limit reached
Next review available in: 27 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
WalkthroughThe PR restructures localized documentation into route-based pages, adds delegated analytics and copy handling, updates GitHub Action publishing, refreshes translations and package metadata, adds WebP routing support, and expands website and manifest validation. ChangesGitHub Action publishing
Localized documentation platform
Website analytics and content controls
Supporting fixes and validation
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR improves Envilder’s public “trust surface” and discoverability by fixing CloudFront static asset rewrites, restructuring the website docs into localized, route-based pages, and aligning marketing/docs claims with shipped behavior. It also adds privacy-safe analytics instrumentation, generates a root GitHub Action manifest for Marketplace-friendly usage, and points SDK package metadata to canonical docs pages.
Changes:
- Fix CloudFront URL rewrite handling for
.webpassets (preserving paths/query strings). - Replace fragment-based docs with a manifest-driven, localized docs route structure (sitemap/SEO coverage via tests).
- Add delegated, privacy-safe analytics attributes + generate a root
action.ymland update docs/metadata accordingly.
Reviewed changes
Copilot reviewed 44 out of 44 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/website/static-site.test.ts | Expand SEO coverage for docs routes |
| tests/iac/lib/stacks/cloudfrontUrlRewrite.test.ts | Add .webp rewrite regression test |
| tests/iac/lib/stacks/snapshots/staticWebsiteStack.test.ts.snap | Snapshot update: include .webp extension |
| tests/envilder/apps/gha/ActionManifest.test.ts | Assert root manifest matches subdir |
| src/website/src/scripts/analytics.ts | Delegated analytics + safe copy handler |
| src/website/src/pages/es/docs/[...slug].astro | ES docs article routing |
| src/website/src/pages/es/docs.astro | ES docs hub uses DocsPage |
| src/website/src/pages/docs/[...slug].astro | EN docs article routing |
| src/website/src/pages/docs.astro | EN docs hub uses DocsPage |
| src/website/src/pages/ca/docs/[...slug].astro | CA docs article routing |
| src/website/src/pages/ca/docs.astro | CA docs hub uses DocsPage |
| src/website/src/layouts/BaseLayout.astro | Load delegated analytics script |
| src/website/src/i18n/types.ts | Extend i18n types for docs/providers |
| src/website/src/i18n/localized-routes.ts | Add localized docs route map |
| src/website/src/i18n/es.ts | Align ES claims + add docs strings |
| src/website/src/i18n/en.ts | Align EN claims + add docs strings |
| src/website/src/i18n/docs-routes.ts | New docs route manifest + legacy hashes |
| src/website/src/i18n/ca.ts | Align CA claims + add docs strings |
| src/website/src/components/ThemeSwitcher.astro | Remove old GA theme event |
| src/website/src/components/Sdks.astro | Add analytics attrs + content IDs |
| src/website/src/components/Providers.astro | Remove GCP examples; add roadmap link |
| src/website/src/components/Navbar.astro | Route-based docs dropdown + analytics |
| src/website/src/components/HowItWorks.astro | Add runtime/package analytics + content IDs |
| src/website/src/components/Hero.astro | Replace unsafe example secret + analytics |
| src/website/src/components/GitHubAction.astro | Add content IDs for copy tracking |
| src/website/src/components/GetStarted.astro | Copy-to-clipboard install buttons + analytics |
| src/website/src/components/Footer.astro | Route-based docs links; remove inline tracking |
| src/website/src/components/DocsShell.astro | New docs shell layout/navigation |
| src/website/src/components/DocsPage.astro | Page wrapper for hub/articles |
| src/website/src/components/DocsHub.astro | Docs hub cards + legacy hash redirect |
| src/website/src/components/DocsArticle.astro | Split docs into route-specific articles |
| src/website/src/components/CodeBlock.astro | Delegate copy + analytics metadata |
| src/sdks/python/pyproject.toml | Point metadata to canonical docs |
| src/sdks/nodejs/package.json | Update homepage/tags for discoverability |
| src/sdks/dotnet/Envilder.csproj | Update package URL/tags/description |
| src/iac/lib/stacks/cloudfront-url-rewrite.js | Treat .webp as static extension |
| scripts/build-github-action.mjs | Generate root action.yml on build |
| README.md | Update claims + use @v0 action tag |
| package.json | Verify action bundle + root manifest |
| github-action/README.md | Update usage/examples and claims |
| docs/github-action.md | Update docs for tagged/prebuilt action |
| docs/CHANGELOG.md | Update GHA “package” link wording |
| action.yml | New root composite action manifest |
| .github/skills/website-content-strategy/SKILL.md | Align website messaging guidance |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (5)
src/website/src/components/DocsShell.astro (1)
28-40: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDo not navigate on every
changeevent of the select.
onchange="window.location.assign(this.value)"navigates as soon as the value changes. When a keyboard user moves through the options with arrow keys, the browser fireschangefor each option and the page navigates before the user commits a choice. The inline handler also requiresunsafe-inlineif a script-src CSP is added later.Replace the select with the same link list used in the sidebar, or attach a script that navigates only after an explicit commit.
♻️ Option: render a link list on mobile instead of a select
<div class="docs-mobile-select"> - <label class="sr-only" for="docs-route-select">{t.docs.mobileNavigation}</label> - <select id="docs-route-select" onchange="window.location.assign(this.value)"> - {articleRoutes.map((route) => ( - <option - value={localizedPath(lang, route.path)} - selected={route.key === routeKey} - > - {t.docs.pages[route.key].navLabel} - </option> - ))} - </select> + <nav aria-label={t.docs.mobileNavigation}> + <ul> + {articleRoutes.map((route) => ( + <li> + <a + href={localizedPath(lang, route.path)} + aria-current={route.key === routeKey ? 'page' : undefined} + > + {t.docs.pages[route.key].navLabel} + </a> + </li> + ))} + </ul> + </nav> </div>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/website/src/components/DocsShell.astro` around lines 28 - 40, Replace the mobile select and its inline onchange handler in DocsShell with the existing sidebar-style link list, using localizedPath(lang, route.path) for each destination and preserving the current route indication and navigation labels.src/website/src/components/DocsArticle.astro (1)
1185-1195: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRestore keyboard access to horizontally scrollable tables.
The removed client script previously wrapped tables in an accessible scroll container. On viewports under 769px the table itself scrolls horizontally, and a scrollable region without focus cannot be reached by keyboard. Wrap wide tables in a container with
tabindex="0"androle="region", then move the overflow rule to that container.♻️ Suggested markup and style change
`@media` (max-width: 768px) { - .docs-table { - display: block; - overflow-x: auto; - white-space: nowrap; - } + .table-scroll { + display: block; + overflow-x: auto; + white-space: nowrap; + }Then wrap each
<table class="docs-table">in the markup:<div class="table-scroll" role="region" tabindex="0" aria-label={t.docs.tableScrollLabel}> <table class="docs-table">…</table> </div>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/website/src/components/DocsArticle.astro` around lines 1185 - 1195, Update the table markup in DocsArticle to wrap each table.docs-table in a .table-scroll container with role="region", tabindex="0", and the localized t.docs.tableScrollLabel aria-label. Move the mobile horizontal overflow and related scrolling styles from .docs-table to .table-scroll, while preserving the existing table layout and responsive behavior.src/website/src/components/DocsHub.astro (1)
13-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDerive the fallback path and the locale list at build time.
The script hard-codes
/docs/github-action/and the localesca|es. If the manifest path changes or a locale is added, legacy hash redirects break with no build error. Pass both values throughdefine:vars, which already serializes plain data.♻️ Proposed change
-const legacyHashes = Object.fromEntries( +const ghaPath = docsRouteManifest.find((route) => route.key === 'github-action')?.path; +const localePrefixes = ['ca', 'es']; +const legacyHashes = Object.fromEntries( docsRouteManifest.flatMap((route) => route.legacyHashes.map((hash) => [hash, route.path]), ), );-<script define:vars={{ legacyHashes }}> +<script define:vars={{ legacyHashes, ghaPath, localePrefixes }}> const hash = window.location.hash.slice(1).toLowerCase(); const targetPath = - legacyHashes[hash] ?? (hash.startsWith('gha-') ? '/docs/github-action/' : null); + legacyHashes[hash] ?? (hash.startsWith('gha-') ? ghaPath : null); if (targetPath) { - const locale = window.location.pathname.match(/^\/(ca|es)(?=\/|$)/)?.[1]; + const locale = localePrefixes.find( + (prefix) => + window.location.pathname === `/${prefix}` || + window.location.pathname.startsWith(`/${prefix}/`), + );Replace
localePrefixeswith the shared locale list from the i18n module if one is exported.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/website/src/components/DocsHub.astro` around lines 13 - 46, Update DocsHub.astro to derive the GitHub Action fallback path and locale prefixes at build time instead of hard-coding them in the client script. Use the manifest’s existing route path for the gha- fallback, obtain the shared locale list from the i18n module (exporting it if needed), and pass both plain values through define:vars for runtime redirect construction.src/website/src/pages/ca/docs/[...slug].astro (1)
8-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUnused
routeKeyprop fromgetStaticPaths.
getStaticPathsreturnsprops: { routeKey: route.key }, but this file never readsAstro.props. Instead, it re-derives the route fromAstro.params.slugwithgetDocsArticleRoute. Remove the unused prop, or use it directly instead of performing a second lookup.♻️ Proposed simplification
export function getStaticPaths() { return docsRouteManifest .filter((route) => route.key !== 'hub') .map((route) => ({ params: { slug: route.path.replace(/^\/docs\/|\/$/g, '') }, - props: { routeKey: route.key }, })); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/website/src/pages/ca/docs/`[...slug].astro around lines 8 - 24, Remove the unused routeKey value from getStaticPaths, or update the page to consume Astro.props.routeKey and avoid the duplicate getDocsArticleRoute lookup; keep the existing route validation and DocsPage routeKey behavior intact.src/website/src/pages/docs/[...slug].astro (1)
5-21: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winTriplicated
getStaticPathsand route-resolution logic across locale files. All three catch-all documentation route files repeat the same filter/map overdocsRouteManifestand the samegetDocsArticleRouteresolution with an identical error path; only thelangliteral and import depth differ.
src/website/src/pages/docs/[...slug].astro#L5-L21: extract thegetStaticPathsbody and theroute/throwresolution block into a shared helper indocs-routes.ts(for examplegetLocalizedDocsPaths()/resolveDocsRoute(slug)), then call it here.src/website/src/pages/ca/docs/[...slug].astro#L8-L24: call the same shared helper here instead of repeating the logic; also drop the unusedprops: { routeKey: route.key }since it is never read fromAstro.propsin this file.src/website/src/pages/es/docs/[...slug].astro#L8-L24: call the same shared helper here instead of repeating the logic.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/website/src/pages/docs/`[...slug].astro around lines 5 - 21, Extract the duplicated docsRouteManifest path generation and getDocsArticleRoute resolution into shared helpers in docs-routes.ts, preserving the existing filtering, slug mapping, and unknown-route error behavior. Update src/website/src/pages/docs/[...slug].astro (lines 5-21), src/website/src/pages/ca/docs/[...slug].astro (lines 8-24), and src/website/src/pages/es/docs/[...slug].astro (lines 8-24) to use those helpers; in the ca file, also remove the unused routeKey props.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/github-action.md`:
- Around line 347-358: Update the release procedure in the “Commit the action
bundle referenced by action.yml” section to stage the generated root action.yml
alongside github-action/dist/index.js. Ensure the documented commit includes
both the bundle and root manifest so generated metadata and runs.main remain
synchronized.
In `@src/website/src/components/CodeBlock.astro`:
- Around line 19-27: Remove the 'code-example' default from contentId in
CodeBlock.astro and make the prop required, then update every CodeBlock call
site—including the three non-SDK usages in HowItWorks.astro—to provide a
distinct stable contentId value.
In `@src/website/src/components/DocsArticle.astro`:
- Around line 853-866: Update the locale documentation translation definitions
used by DocsArticle and the docs shell so every docsRouteKeys entry has a pages
record, including getting-started, aws-ssm, azure-key-vault, map-file, cli-pull,
cli-push, github-action, sdk-dotnet, sdk-python, and sdk-nodejs for ca and es,
plus any missing entries for en. Provide each page’s navLabel while preserving
the existing hub entry and locale-specific translations.
In `@src/website/src/components/Footer.astro`:
- Around line 111-119: Move the conditional click_github analytics attribute
from the documentation link rendering to the community link map, using
l.href.includes('github.com') as the condition and retaining
data-analytics-placement="footer-community". Ensure links.community renders the
tracking metadata while links.docs remains without the GitHub event.
In `@src/website/src/i18n/docs-routes.ts`:
- Around line 110-120: Update DocsHub.astro to import and use
resolveLegacyDocsHash when handling legacy documentation hashes, replacing the
inline gha-* check and hard-coded GitHub Actions route. Preserve the undefined
fallback so non-hub routes continue rendering correctly, and derive redirects
from the shared docs route manifest.
In `@tests/website/static-site.test.ts`:
- Around line 252-263: Update the forbiddenClaims matching in the static-site
test to compare normalized lowercase values, converting both the combined html
content and each claim before checking inclusion. Preserve the existing actual
filtering and forbidden-claim assertions.
---
Nitpick comments:
In `@src/website/src/components/DocsArticle.astro`:
- Around line 1185-1195: Update the table markup in DocsArticle to wrap each
table.docs-table in a .table-scroll container with role="region", tabindex="0",
and the localized t.docs.tableScrollLabel aria-label. Move the mobile horizontal
overflow and related scrolling styles from .docs-table to .table-scroll, while
preserving the existing table layout and responsive behavior.
In `@src/website/src/components/DocsHub.astro`:
- Around line 13-46: Update DocsHub.astro to derive the GitHub Action fallback
path and locale prefixes at build time instead of hard-coding them in the client
script. Use the manifest’s existing route path for the gha- fallback, obtain the
shared locale list from the i18n module (exporting it if needed), and pass both
plain values through define:vars for runtime redirect construction.
In `@src/website/src/components/DocsShell.astro`:
- Around line 28-40: Replace the mobile select and its inline onchange handler
in DocsShell with the existing sidebar-style link list, using
localizedPath(lang, route.path) for each destination and preserving the current
route indication and navigation labels.
In `@src/website/src/pages/ca/docs/`[...slug].astro:
- Around line 8-24: Remove the unused routeKey value from getStaticPaths, or
update the page to consume Astro.props.routeKey and avoid the duplicate
getDocsArticleRoute lookup; keep the existing route validation and DocsPage
routeKey behavior intact.
In `@src/website/src/pages/docs/`[...slug].astro:
- Around line 5-21: Extract the duplicated docsRouteManifest path generation and
getDocsArticleRoute resolution into shared helpers in docs-routes.ts, preserving
the existing filtering, slug mapping, and unknown-route error behavior. Update
src/website/src/pages/docs/[...slug].astro (lines 5-21),
src/website/src/pages/ca/docs/[...slug].astro (lines 8-24), and
src/website/src/pages/es/docs/[...slug].astro (lines 8-24) to use those helpers;
in the ca file, also remove the unused routeKey props.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 64fa82e7-e607-4e90-8ebc-b6b07625e053
⛔ Files ignored due to path filters (6)
.github/skills/website-content-strategy/SKILL.mdis excluded by none and included by noneREADME.mdis excluded by none and included by noneaction.ymlis excluded by none and included by nonegithub-action/README.mdis excluded by none and included by nonepackage.jsonis excluded by none and included by nonetests/iac/lib/stacks/__snapshots__/staticWebsiteStack.test.ts.snapis excluded by!**/*.snapand included bytests/**
📒 Files selected for processing (38)
docs/CHANGELOG.mddocs/github-action.mdscripts/build-github-action.mjssrc/iac/lib/stacks/cloudfront-url-rewrite.jssrc/sdks/dotnet/Envilder.csprojsrc/sdks/nodejs/package.jsonsrc/sdks/python/pyproject.tomlsrc/website/src/components/CodeBlock.astrosrc/website/src/components/DocsArticle.astrosrc/website/src/components/DocsHub.astrosrc/website/src/components/DocsPage.astrosrc/website/src/components/DocsShell.astrosrc/website/src/components/Footer.astrosrc/website/src/components/GetStarted.astrosrc/website/src/components/GitHubAction.astrosrc/website/src/components/Hero.astrosrc/website/src/components/HowItWorks.astrosrc/website/src/components/Navbar.astrosrc/website/src/components/Providers.astrosrc/website/src/components/Sdks.astrosrc/website/src/components/ThemeSwitcher.astrosrc/website/src/i18n/ca.tssrc/website/src/i18n/docs-routes.tssrc/website/src/i18n/en.tssrc/website/src/i18n/es.tssrc/website/src/i18n/localized-routes.tssrc/website/src/i18n/types.tssrc/website/src/layouts/BaseLayout.astrosrc/website/src/pages/ca/docs.astrosrc/website/src/pages/ca/docs/[...slug].astrosrc/website/src/pages/docs.astrosrc/website/src/pages/docs/[...slug].astrosrc/website/src/pages/es/docs.astrosrc/website/src/pages/es/docs/[...slug].astrosrc/website/src/scripts/analytics.tstests/envilder/apps/gha/ActionManifest.test.tstests/iac/lib/stacks/cloudfrontUrlRewrite.test.tstests/website/static-site.test.ts
💤 Files with no reviewable changes (1)
- src/website/src/components/ThemeSwitcher.astro
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 44 out of 44 changed files in this pull request and generated no new comments.
Suppressed comments (6)
src/website/src/components/DocsArticle.astro:26
- These template literals use
\as a line-continuation escape, which removes the newline (and the backslash) from the resulting string. That means the docs code block will render as one long line instead of a multi-line command with visible\\continuations.
This issue also appears in the following locations of the same file:
- line 28
- line 31
src/website/src/components/DocsArticle.astro:29
- Same line-continuation issue here: a single backslash at EOL escapes the newline inside the template literal, so the rendered snippet loses the intended formatting.
src/website/src/components/DocsArticle.astro:34 - Same line-continuation issue here: the single
\at end-of-line escapes the newline inside the template literal, so the code snippet will not render with line breaks/backslashes as intended.
tests/website/static-site.test.ts:6 localizedRoutesis imported but never used in this test file, which can trigger unused-import lint failures and adds noise to the test setup.
src/website/src/components/GetStarted.astro:253- The
.install-codestyle block repeatspaddingandcolorassignments, which makes the CSS harder to maintain and can hide future edits (one of the duplicates may get updated while the other is missed).
padding: 0;
text-align: left;
padding: 0;
color: var(--color-text);
src/website/src/components/DocsShell.astro:35
- When
DocsShellis rendered for the docs hub (routeKey === 'hub'), none of the<option>entries can matchrouteKey, so the select defaults to the first item without reflecting the current page. This is confusing on mobile where the select is the primary navigation control.
{articleRoutes.map((route) => (
<option
value={localizedPath(lang, route.path)}
selected={route.key === routeKey}
>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 44 out of 44 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/website/src/components/DocsShell.astro:31
- The mobile docs route
<select>excludes thehubroute, so whenrouteKeyis 'hub' no<option>is marked selected and the browser will default to the first article, misrepresenting the current page. Including the hub route in the options keeps the selection accurate.
{articleRoutes.map((route) => (
tests/website/static-site.test.ts:10
localizedDocsPageshard-codes the language list (['en','ca','es']) even though the test already depends onlocalizedLanguages()for hreflang expectations. This can silently diverge iflocalizedRouteschanges (e.g., adding/removing locales for a docs path), causing the test to miss/over-report missing pages.
Summary
Improves Envilder's discoverability and trust surface by fixing a CloudFront asset rewrite defect, aligning public claims with shipped behavior, and expanding the documentation into focused localized routes.
Adds privacy-safe conversion tracking, Marketplace-ready GitHub Action metadata, and canonical registry metadata for all runtime SDKs.
Changes
Testing
pnpm testpassespnpm lintpassespnpm verify:ghapassesRelated
N/A
Summary by CodeRabbit
New Features
Bug Fixes