Skip to content

Risks & Improvements

The platform is intentionally ambitious — one shell, many independent teams, two integration paths, a dynamic registry, a self-service deployment pipeline, AI-first tooling. This is an honest catalogue of where it’s brittle today and what’s prioritized to fix next.

Out of scope: the local link: workspace dependency issue (tracked separately). The Artifactory publishing pipeline itself — an auto-opened “Version Packages” PR (.github/workflows/version-packages.yml) followed by each package’s own Jenkins pipeline (nodeLibraryPipeline) publishing on main — isn’t listed as a risk below because merging that PR is the only manual step left; see whisker-platform/README.md (“How deployments work”) for the full flow.

1. The manifest schema can’t validate everything

Section titled “1. The manifest schema can’t validate everything”

It catches structural errors (required fields, enums, mutual exclusivity of integration kinds, name uniqueness) but can’t catch a componentPath that doesn’t resolve to an actual export in the deployed bundle, a dead iframe URL, or a Slack channel nobody’s in. These fail only at runtime — the nightly Playwright run is the only systemic catch today. Improvement: a CI “dry-load” step that verifies every componentPath against the freshly-built remoteEntry.js before publishing.

merge → CI build → S3 upload → Lambda → DynamoDB → next page load — each hop is normally seconds but can stall, and there’s no “published successfully” notification back to the developer. Improvement: wire the publisher Lambda to post to manifest.notifications.slack.channel on success and on DLQ rejection — the schema slot already exists.

3. pinnedHostContractVersion is informational only

Section titled “3. pinnedHostContractVersion is informational only”

The host publishes a live, queryable whisker-contract.json, so the diff path is real — but the host doesn’t yet refuse to mount a module whose pinned contract is older than its declared minimum. Also: contract regeneration is a husky pre-commit hook, so git commit --no-verify can land drift that only PR review catches today. Improvement: make the host refuse to mount an out-of-date module with a clear upgrade message; ship thundercats update --contract; add a host CI step that re-runs the generator and fails the build on unexpected diff.

4. Iframe postMessage protocol has no compile-time surface

Section titled “4. Iframe postMessage protocol has no compile-time surface”

Hand-coded on both sides — renaming a message or changing a payload shape is a silent break with no shared type either side can import. Improvement: a @nike/whisker-iframe-protocol types-only package consumed by both the host and the kit’s iframe-consumer template.

Federation modules load a host-provided React, MUI, and react-router at runtime. A host major bump (React 19, MUI v6) can break a module that hasn’t rebuilt against the new peer set. The host now publishes sharedPackages in its contract, so the input data for a drift check already exists — it’s just not wired into doctor yet. Improvement: a whisker doctor check that fetches <host>/whisker-contract.json#sharedPackages and warns on a major-version mismatch against the module’s own package.json.

Okta + BERM auth is registered to this origin; changing it invalidates every developer session. Enforced today only by convention (strictPort: true in Vite config + doctor checks) — a stray vite.config edit could still land in a release. Improvement: a runtime assertion in whisker-host that throws on boot if the resolved port is ever anything other than 3001.

7. Admin-aware merge is correct only if name is stable

Section titled “7. Admin-aware merge is correct only if name is stable”

mergeManifestForUpsert matches components by name; a rename is a delete+add, which loses any admin-controlled enabled/allowedPersonas state for that component with no “renamed” diagnostic. Improvement: an optional id on component buckets, preferred over name when present.

whisker-host still calls sim-bff for GET /manifests/v1 and the legacy BERM login/refresh routes — platform concerns that don’t belong in an inventory BFF. See Manifest pipeline & deployment for the extraction plan. The manifest endpoint’s public-read posture will carry forward unchanged, so no new local-dev auth setup is needed either before or after the migration.

9. whisker-config-panel doesn’t exist yet

Section titled “9. whisker-config-panel doesn’t exist yet”

The Lambda merge preserves admin-edited enabled/allowedPersonas, but the admin UI that lets a human set them isn’t built. Today the only way to flip those values in production is a direct DynamoDB write — unsafe and unmonitored.

NikeStoreProvider.config is store-level only; rwe-core’s per-athlete deledge flags have no whisker-host equivalent yet. Needs a decision (manifest userOverrides, a flags service, or a BFF proxy) before a full rwe-core migration.

11. The devkit panel and its 8 compliance checks are design-only

Section titled “11. The devkit panel and its 8 compliance checks are design-only”

whisker doctor runs real checks today; the eight-check live panel on port 3009 is fully speculative — no server, no UI, nothing running on a developer’s machine. See Automation, devkit & E2E. Even once it ships, checks stay advisory by design (self-service over gating), which is an accepted long-term cost.

12. Thundercats Phase 2D is still pending, plus two untracked deviations

Section titled “12. Thundercats Phase 2D is still pending, plus two untracked deviations”

The orchestrator + three specialists ship at platform-docs depth; @nike/create-whisker writes the agent-context files itself as the last scaffold step (so create no longer needs to chain a separate init call — init is reserved for onboarding a pre-existing repo). Pending: the external-skills pipeline, multi-repo scope, and --contract. Two smaller deviations from the original plan, discovered during a docs cleanup and now tracked in whisker-platform/DEFERRED.md:

  • The tc short binary alias was decided but never shipped — bin only exposes thundercats.
  • thundercats update was supposed to self-update by default; today only the separate thundercats upgrade command does.

13. Service-kit publish gap during the pilot window

Section titled “13. Service-kit publish gap during the pilot window”

thundercats create service writes a working template that depends on @nike/whisker-service-kit — but the kit itself is deliberately excluded from the first Artifactory pilot (.changeset/config.json#ignore) since the pilot’s dependency graph doesn’t reach it. Between the pilot ship date and the follow-up release, a scaffolded service can’t pnpm install outside this workspace. Short-lived by design — RELEASE_RUNBOOK.md’s “post-pilot tidy-up” lifts the ignore immediately after.

14. The sync workflow depends on /manifests/v1 staying public

Section titled “14. The sync workflow depends on /manifests/v1 staying public”

sync-deployed-registry.yml calls the BFF from a GitHub-hosted runner with no Nike credentials — this works only because the endpoint is designed to be public. If it were ever put behind auth, the sync workflow, local whisker dev against remote BFFs, and the “no local credential setup” promise all break at once. Improvement: when whisker-bff ships, codify the public-read contract in its README and add an integration test asserting GET /manifests/v1 returns 200 with no auth header.

  1. Validate the v0.1.0 pilot publish + install on a clean machine, then lift @nike/whisker-service-kit out of the changesets ignore list (closes #13).
  2. Complete the whisker-bff extraction (#8), carrying forward the public-read posture of /manifests/v1 with a locking integration test (#14).
  3. Build whisker-config-panel (#9).
  4. Wire the publisher Lambda → Slack notifications (#2).
  5. Ship the whiskerTest fixture + ./e2e kit export (see Automation, devkit & E2E).
  6. Implement the devkit panel server + UI (#11) — the highest-leverage automation piece still on the board.
  1. pinnedHostContractVersion enforcement + thundercats update --contract
    • a host CI drift check (#3).
  2. Host shared-deps drift check in whisker doctor / thundercats audit (#5).
  3. Dry-load CI step for componentPath validation (#1).
  4. @nike/whisker-iframe-protocol types package (#4).
  5. Per-athlete feature overrides decision (#10).
  6. Optional id on component buckets (#7).
  1. Thundercats Phase 2D as one slice — external-skills pipeline, cache, quality gates, multi-repo fan-out, add-skill / diff, plus the two small deviations in #12 (tc alias, self-update-on-update).
  2. WCL AGENTS.md hierarchy polish, so the Tier 1 specialist registry can flip its WCL entry from placeholder: true.
  3. Scaffolder version-parity for service + iframe-consumer scaffolds.
  4. Widget lifecycle convention in WCL (per-operator dismiss persistence — see rwe-core vs whisker-host for the prior art).
  5. whisker-lambda-kit for serverless workloads.
  6. Virtual store engine for QA session simulation.
Owner Cadence Cost
Platform team Every host release Confirm schemaVersion/moduleKitMinVersion reflect real breaking changes; confirm nightly is green
Platform team Every kit release Dry-run the changeset payload; regression-test against ≥1 federation module and ≥1 iframe consumer
Platform team Every Lambda change Re-verify DLQ replay + admin-merge invariants
Platform team Monthly / Quarterly Audit orphan DynamoDB rows; review the compliance-check inventory
Module teams Continuously thundercats upgrade (then install-skill) for CLI/skill updates; thundercats audit/update for a repo’s .cursor/
Module teams Weekly pnpm update @nike/whisker-module-kit if update-check flags one
Module teams On host shared-dep bumps Rebuild + redeploy against the new peer set
End users Effectively zero — dashboard layout, dark mode, language, and inventory cache all persist and survive refresh automatically
  • One Zod schema, imported by everything that touches a manifest — the reason “valid locally” really does mean “valid at ingest.”
  • whisker-contract.json as a versioned, queryable artifact.
  • Admin-editable fields preserved across redeploys.
  • Tiny remoteSafeSelectors surface — resist expanding it casually.
  • Manifest endpoint stays publicly readable.
  • Host port 3001 is sacred.
  • Dynamic origin allow-list from the registry — no hand-maintained CORS lists.
  • Doctor prints exact remediation commands.