feat: add experience-auditor showcase example [EXT-7476]#11010
Conversation
⚔️ The Gauntlet — ✅ 0 Blockers · all advisory findings resolved🤖 AI-powered · your code, through the lanes Note Delta re-review — 2026-07-09,
Findings from the original review (2026-06-04) and enhancement re-review (2026-06-08)
New findings — SDK migration re-review (2026-07-09)
The Gauntlet clears this PR for merge — nothing here blocks. This cycle's diff is a mechanical SDK migration: the real, now-published One more thing worth a look before merge, outside the code lanes: the PR body's "41/41 tests pass" is stale — the suite is 36 tests now, matching the README. 🤖 Machine-readable findings (for agents){
"tool": "gauntlet",
"schema": "v1",
"reviewed_ref": "d8784a7df",
"verdict": { "blockers": 0, "concerns": 4, "nits": 2, "security": "clean" },
"findings": [
{ "severity": "concern", "location": "src/audit/types.ts:16-24", "lens": "adversarial-review / Hidden Assumptions", "confidence": 88, "claim": "AutoFix JSDoc still describes the removed write-and-confirm flow, contradicting every other doc updated in this migration.", "recommendation": "Rewrite the JSDoc to describe both fix kinds as read-only advice." },
{ "severity": "concern", "location": "src/audit/rules.ts:208", "lens": "adversarial-review / Failure Scenarios", "confidence": 85, "claim": "brokenBindingRule's finding text says the reference 'does not resolve' but the check is now purely structural (missing entryId only).", "recommendation": "Change the detail text to describe a missing entryId, not an unresolved reference." },
{ "severity": "concern", "location": "README.md:151-152", "lens": "adversarial-review / Failure Scenarios", "confidence": 85, "claim": "README demo instructions describe an edit affordance (accept or edit a suggested fix) that no longer exists.", "recommendation": "Change to 'review a suggested fix' — no edit/accept action remains." },
{ "severity": "concern", "location": "src/audit/collect.ts", "lens": "adversarial-review / Hidden Assumptions", "confidence": 82, "claim": "A live host with no experience-tree sync yet renders identically to a genuinely clean experience — no UI signal distinguishes the two.", "recommendation": "Surface nodeCount === 0 as a distinct 'no data yet' state, not the same as a clean score." },
{ "severity": "nit", "location": "src/audit/types.ts:26,30", "lens": "code-quality-standards / Concise", "confidence": 80, "claim": "propertyKey on AutoFix is dead data since the write path that read it was removed.", "recommendation": "Drop propertyKey from both AutoFix variants." },
{ "severity": "nit", "location": "src/locations/ExperienceToolbar.spec.tsx:62,64", "lens": "adversarial-review / Hidden Assumptions", "confidence": 80, "claim": "Test title/comment still cite app-sdk 4.59 though the pin moved to ^4.63.1 in this range.", "recommendation": "Drop the version number — the write-path removal is permanent, not version-specific." }
]
}🎮 The Gauntlet · an AI review harness built by Josh C.S. Lewis · reviewed at |
301da2a to
3d005cb
Compare
24544c6 to
d9d323b
Compare
Wiz Scan Summary
To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension. |
Add a polished, customer-facing example app for the ExO toolbar location that goes beyond the minimal starter (EXT-7365). Experience Auditor runs inside the Experience Editor toolbar and continuously audits the experience for accessibility, SEO, and content-completeness issues. It demonstrates the standout capability of the toolbar location: live, selection-aware tooling that reads and mutates the experience tree. A scored dashboard lists findings; clicking one locates the offending component on the canvas (selection.set + selection.highlight), safe deterministic fixes apply in place via setContentProperty (permission- checked, with notifier feedback), and publish is gated on outstanding errors. Audit rules are pure functions over an SDK-independent node shape, fully unit-tested without a live SDK; the SDK boundary is isolated in a thin collector. Typed with ExperienceEditorToolbarAppSDK (app-sdk@4.58.0). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…476] Address code review on the experience-auditor showcase: - alt-text rule now requires an asset-shaped value, not just an image-ish key, so string fields (iconName, logoText, assetId) no longer emit false errors that block the publish gate - heading rule excludes meta-ish keys so metaTitle/seoTitle no longer double-fire across the heading and SEO rules - add a fix-apply integration test (setContentProperty + re-audit) so the documented fix path is actually covered - add regression tests for the false-positive and double-fire cases - document the onChange re-audit no-debounce simplification and why handleFix re-audits explicitly Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…perience-auditor [EXT-7476]
…solution [EXT-7476]
…7476]
4.58.1 reshaped ComponentPropertyDescriptor.binding to the typed
`Binding` union (EntryBinding | ManualBinding), replacing the old
`{ sourceType, entryId }` ComponentPropertyBinding shape.
- rules.ts / collect.ts: gate entry bindings on `binding.type === 'entry'`
(was `binding.sourceType === 'entry'`); `entryId` reads now narrow off
the EntryBinding arm.
- Specs: use the EntryBinding fixture shape `{ type, entryId, fieldId }`.
- Bump @contentful/app-sdk 4.58.0 -> 4.58.1.
Verified: tsc --noEmit clean, 41 tests passing against 4.58.1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…7476] - Bump @contentful/app-sdk 4.58.1 -> 4.58.2 to track the latest published ExO surface (DA definition-only trim, ui-extensions-sdk#2592). - Run prettier (2.8.8, matching CI) on ConfigScreen.tsx to clear the prettier-check.sh violation failing apps-test. 41 tests green against 4.58.2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bump @contentful/app-sdk 4.58.2 → ^4.59.0 and adapt the example to the removed surface: - resolveEntryBinding is gone; brokenBindingRule now uses the structural !binding.entryId check it already fell back to (drop resolveBindings + ResolvedBinding type + the resolution-aware test cases). - setContentProperty is gone; node content is DA-only on 4.59 with no content-string write path. The auditor becomes read-only: findings still surface their derived fix as advice (SuggestedFix renders the value read-only), but the one-click apply path is removed. Auditor still detects + reports + locates; it no longer mutates content.
…le [EXT-7497] floor bump ^4.60.0; Exo*→Experience* + sdk.exo→sdk.experiences; mockExo.ts→mockExperiences.ts.
…XT-7476] The README described a write-capable "One-click fixes" feature (findings writing back via getNode().setContentProperty()), but the app has no such method and treats all fixes as read-only advice — the app-sdk surface exposes no content-property write. Rewrites the headline, feature list, and fixes section to match the code: fixes are surfaced as advice the author applies manually. Also corrects the app-sdk version (4.58.0 -> 4.60.0, where sdk.experiences first exists), drops customer-facing "ExO" phrasing, notes the getRootNodes() empty-stub caveat, and fixes the test count (40 -> 37). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
app-sdk 4.63.1 removes the experience getUiMode()/onUiModeChanged() surface. Drop the mode state, the form-mode notice, and the mode condition on the Locate action, which now keys solely off the capability probe for the selection surface. Bump the app-sdk pin to ^4.63.1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
1a37b0c to
d8784a7
Compare
- Rewrite AutoFix JSDoc and drop dead propertyKey field (write path gone) - Fix broken-binding finding text to match structural entryId check - README: demo is review-only; broken-binding table; no-tree-data note - Distinguish empty getRootNodes() from a genuinely clean experience - Drop version-specific wording from advisory-fix test title Co-authored-by: Cursor <cursoragent@cursor.com>
Note
This example targets an App SDK location that's currently in limited/internal availability. The Experience Editor toolbar location isn't generally available yet, so an app built from this example won't be installable in most spaces for the time being. The example is published now so the code and patterns are here to explore and build against ahead of a wider rollout.
Especially curious? If you'd like to try the toolbar location early, reach out to Contentful support — we're happy to talk through early-access options.
EXT-7476 · sibling of EXT-7365 under epic EXT-6925
Summary
examples/experience-auditor— a polished, customer-facing showcase app for the Experience Editor toolbar location that goes well beyond the minimal starter (PR feat: add experience-toolbar starter example [EXT-7365] #11009 / EXT-7365). Scaffoldable vianpx create-contentful-app --example experience-auditor.What it demonstrates
getRootNodes()→getProperties(), re-running onsdk.experiences.experience.onChange(). Five rules ship: missing alt text, empty required headings, empty SEO metadata, broken entry bindings (structuralentryIdcheck), and a cross-node heading-order check that flags skipped levels (e.g. H2 → H4).selection.set()+selection.highlight(nodeId, { flash, scrollIntoView })jumps to the offending component when the host backs the selection surface; gracefully disabled with an inline explanation otherwise.experience.publish()(permission-checked viasdk.access.can(), confirmed throughsdk.notifier) is blocked while any error-level finding remains.Design
Audit rules are pure functions over an SDK-independent
CollectedNodeshape (src/audit/rules.ts), so they are fully unit-tested without a live SDK. SDK coupling is confined to a thin collector (src/audit/collect.ts), which walks the tree read-only viagetRootNodes()/getProperties(), and a capability probe (src/audit/capabilities.ts), which detects what the host backs (e.g. selection). Shared key-matching lives insrc/audit/keys.ts; suggested-fix derivation is a separate pure module (src/audit/fixes.ts). Adding a rule = dropping anotherAuditRuleintoAUDIT_RULES(or, for an order-sensitive check, an evaluator the engine runs over the full node list).A dev-only demo mode (
npm start, then open/?demo) renders the panel against a seeded in-memorysdk.experiences, so the audit → suggested-fix → re-score loop is clickable without a live host. The demo scaffolding is dynamically imported and stripped from production builds.Typed with
ExperienceEditorToolbarAppSDKfrom@contentful/app-sdk@^4.63.1. Follows repo conventions (React + TS + Vite + Vitest + Forma 36), mirroringexamples/typescript.Verification status
tsc --noEmitclean against the published app-sdk types.vite buildclean.sdk.experiencesis still rolling out (EXT-7363/EXT-7364). README states this. API shapes match the published types exactly.Test plan
npm install && npm run buildinexamples/experience-auditorsucceedsnpm run test:cipassesnpx create-contentful-app --example experience-auditorscaffolds a working project (after merge tomaster)experience-toolbarlocation, open an experience with a missing-alt-text image, confirm the finding appears, Locate highlights the node, and publish unblocks once errors are resolvedGenerated with Claude Code